Dillo v3.2.0-143-gabad1053
Loading...
Searching...
No Matches
image.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
5 * Copyright 2024-2025 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22
23//#define DEBUG_LEVEL 1
24#include "image.hh"
25#include "dlib/dlib.h"
26#include "../lout/msg.h"
27#include "../lout/misc.hh"
28#include "../lout/debug.hh"
29
30namespace dw {
31
32using namespace lout;
33
35{
36 shapesAndLinks = new container::typed::List <ShapeAndLink> (true);
37 defaultLink = -1;
38}
39
41{
42 delete shapesAndLinks;
43}
44
46 int x, int y)
47{
48 container::typed::Iterator <ShapeAndLink> it;
49
50 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
51 ShapeAndLink *shapeAndLink = it.getNext ();
52
53 shapeAndLink->shape->draw(view, style, x, y);
54 }
55}
56
58 ShapeAndLink *shapeAndLink = new ShapeAndLink ();
59 shapeAndLink->shape = shape;
60 shapeAndLink->link = link;
61 shapesAndLinks->append (shapeAndLink);
62}
63
65 container::typed::Iterator <ShapeAndLink> it;
66 int link = defaultLink;
67
68 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
69 ShapeAndLink *shapeAndLink = it.getNext ();
70
71 if (shapeAndLink->shape->isPointWithin (x, y)) {
72 link = shapeAndLink->link;
73 break;
74 }
75 }
76
77 return link;
78}
79
81{
82 imageMaps = new container::typed::HashTable <object::Object, ImageMap>
83 (true, true);
84 currentMap = NULL;
85}
86
91
100{
101 currentMap = new ImageMap ();
102 imageMaps->put (key, currentMap);
103}
104
112{
113 currentMap->add (shape, link);
114}
115
123
125 core::style::Style *style, int x, int y)
126{
127 ImageMap *map = imageMaps->get (key);
128
129 if (map)
130 map->draw(view, style, x, y);
131}
132
133int ImageMapsList::link (object::Object *key, int x, int y)
134{
135 int link = -1;
136 ImageMap *map = imageMaps->get (key);
137
138 if (map)
139 link = map->link (x, y);
140
141 return link;
142}
143
144// ----------------------------------------------------------------------
145
146int Image::CLASS_ID = -1;
147
148Image::Image(const char *altText, bool markEmpty)
149{
150 DBG_OBJ_CREATE ("dw::Image");
151 registerName ("dw::Image", &CLASS_ID);
152 this->altText = altText ? dStrdup (altText) : NULL;
153 altTextWidth = -1; // not yet calculated
154 buffer = NULL;
155 bufWidth = bufHeight = -1;
156 clicking = false;
157 currLink = -1;
158 mapList = NULL;
159 mapKey = NULL;
160 isMap = false;
161 useAltStyle = false;
162 this->markEmpty = markEmpty;
163 altStyle = NULL;
164 savedStyle = NULL;
165
166 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
167 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
168}
169
171{
172 if (altText)
173 free(altText);
174 if (buffer)
175 buffer->unref ();
176 if (altStyle)
177 altStyle->unref ();
178 if (savedStyle)
179 savedStyle->unref ();
180 if (mapKey)
181 delete mapKey;
182
184}
185
187{
188 DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
189
190 DEBUG_MSG(1, "-- Image::sizeRequestSimpl() begins\n");
191
192 DEBUG_MSG(1, "Image::sizeRequestImpl border: w=%d h=%d\n",
193 boxDiffWidth(), boxDiffHeight());
194
195 /* First set a naive size based on the image properties if given */
196
197 if (buffer) {
198 /* Restore saved border only if overwritten */
199 if (useAltStyle)
200 setStyle(savedStyle);
201
202 requisition->width = buffer->getRootWidth ();
203 requisition->ascent = buffer->getRootHeight ();
204 requisition->descent = 0;
205 } else {
206 if (markEmpty && !useAltStyle) {
207 savedStyle = getStyle();
208 savedStyle->ref();
209 core::style::StyleAttrs altAttrs = *getStyle();
210 altAttrs.borderWidth.setVal(1);
211 altAttrs.padding.setVal(2);
212 altAttrs.setBorderColor(altAttrs.color);
214 altStyle = core::style::Style::create(&altAttrs);
215 setStyle(altStyle);
216 useAltStyle = true;
217 }
218
219 if (altText && altText[0]) {
220 if (altTextWidth == -1)
221 altTextWidth =
222 layout->textWidth (getStyle()->font, altText, strlen (altText));
223
224 requisition->width = altTextWidth;
225 requisition->ascent = getStyle()->font->ascent;
226 requisition->descent = getStyle()->font->descent;
227 } else {
228 requisition->width = 0;
229 requisition->ascent = 0;
230 requisition->descent = 0;
231 }
232 }
233
234 requisition->width += boxDiffWidth ();
235 requisition->ascent += boxOffsetY ();
236 requisition->descent += boxRestHeight ();
237
238 DEBUG_MSG(1, "Image: initial requisition (with border): w=%d, h=%d\n",
239 requisition->width, requisition->ascent + requisition->descent);
240
241 /* Then correct the size if needed, so it fits within the available space in
242 * the container widget. The correctRequisition() method will take into the
243 * account the preferred aspect ratio. */
244
245 correctRequisition (requisition, core::splitHeightPreserveDescent, true,
246 true);
247
248 DEBUG_MSG(1, "Image: corrected requisition: w=%d, h=%d\n",
249 requisition->width, requisition->ascent + requisition->descent);
250
251 DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
252 requisition->width, requisition->ascent, requisition->descent);
253 DBG_OBJ_LEAVE ();
254}
255
257{
258 int contentWidth;
259 if (buffer)
260 contentWidth = buffer->getRootWidth ();
261 else {
262 if (altText && altText[0]) {
263 if (altTextWidth == -1)
264 altTextWidth =
265 layout->textWidth (getStyle()->font, altText, strlen (altText));
266 contentWidth = altTextWidth;
267 } else
268 contentWidth = 0;
269 }
270
271 int width = contentWidth + boxDiffWidth ();
272
273 // With percentage width, the image may be narrower than the buffer.
274 extremes->minWidth =
275 core::style::isPerLength (getStyle()->width) ? boxDiffWidth () : width;
276
277 // (We ignore the same effect for the maximal width.)
278 extremes->maxWidth = width;
279
280 extremes->minWidthIntrinsic = extremes->minWidth;
281 extremes->maxWidthIntrinsic = extremes->maxWidth;
282
283 correctExtremes (extremes, false);
284
285 extremes->adjustmentWidth =
286 misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
287}
288
290{
291 DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
292 allocation->x, allocation->y, allocation->width,
293 allocation->ascent, allocation->descent);
294
295 DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
296 allocation->x, allocation->y, allocation->width,
297 allocation->ascent, allocation->descent);
298
299 DEBUG_MSG(1, "Image::sizeAllocateImpl border: w=%d h=%d\n",
300 boxDiffWidth(), boxDiffHeight());
301
302
303 int newBufWidth = allocation->width - boxDiffWidth ();
304 int newBufHeight =
305 allocation->ascent + allocation->descent - boxDiffHeight ();
306
307 if (buffer && newBufWidth > 0 && newBufHeight > 0 &&
308 // Save some time when size did not change:
309 (newBufWidth != bufWidth || newBufHeight != bufHeight)) {
310 DBG_OBJ_MSG ("resize", 1, "replacing buffer");
311
312 DEBUG_MSG(1, "Image::sizeAllocateImpl new buffer size: w=%d h=%d\n",
313 newBufWidth, newBufHeight);
314
315 core::Imgbuf *oldBuffer = buffer;
316 buffer = oldBuffer->getScaledBuf (newBufWidth, newBufHeight);
317 oldBuffer->unref ();
318
319 bufWidth = newBufWidth;
320 bufHeight = newBufHeight;
321
322 DBG_OBJ_ASSOC_CHILD (this->buffer);
323 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
324 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
325 }
326
327 DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
328 allocation->x, allocation->y, allocation->width,
329 allocation->ascent, allocation->descent);
330
331 DBG_OBJ_LEAVE ();
332}
333
335{
336 DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
337 // Nothing to do.
338 DBG_OBJ_LEAVE ();
339}
340
342{
343 // BUG: this is wrong for image maps, but the cursor position is unknown.
344 currLink = getStyle()->x_link;
345
346 if (currLink != -1) {
347 (void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
348 }
349 Widget::enterNotifyImpl(event);
350}
351
353{
354 clicking = false;
355
356 if (currLink != -1) {
357 currLink = -1;
358 (void) layout->emitLinkEnter (this, -1, -1, -1, -1);
359 }
360 Widget::leaveNotifyImpl(event);
361}
362
363/*
364 * Return the coordinate relative to the contents.
365 * If the event occurred in the surrounding box, return the value at the
366 * edge of the contents instead.
367 */
369{
370 int ret = event->xWidget - boxOffsetX();
371
372 ret = misc::min(getContentWidth(), misc::max(ret, 0));
373 return ret;
374}
375
377{
378 int ret = event->yWidget - boxOffsetY();
379
380 ret = misc::min(getContentHeight(), misc::max(ret, 0));
381 return ret;
382}
383
385{
386 if (mapList || isMap) {
387 int x = contentX(event);
388 int y = contentY(event);
389
390 if (mapList) {
391 /* client-side image map */
392 int newLink = mapList->link (mapKey, x, y);
393 if (newLink != currLink) {
394 currLink = newLink;
395 clicking = false;
396 /* \todo Using MAP/AREA styles would probably be best */
397 setCursor(newLink == -1 ? getStyle()->cursor :
399 (void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
400 }
401 } else if (isMap && currLink != -1) {
402 /* server-side image map */
403 (void) layout->emitLinkEnter (this, currLink, -1, x, y);
404 }
405 }
406 return true;
407}
408
410{
411 bool ret = false;
412
413 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
414 getStyle()->x_link;
415 if (event->button == 3){
416 (void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
417 event);
418 ret = true;
419 } else if (event->button == 1 || currLink != -1){
420 clicking = true;
421 ret = true;
422 }
423 return ret;
424}
425
427{
428 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
429 getStyle()->x_link;
430 if (clicking) {
431 int x = isMap ? contentX(event) : -1;
432 int y = isMap ? contentY(event) : -1;
433 clicking = false;
434 layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
435 return true;
436 }
437 return false;
438}
439
441 core::DrawingContext *context)
442{
443 int dx, dy;
444 core::Rectangle content, intersection;
445
446 drawWidgetBox (view, area, false);
447
448 if (buffer) {
449 dx = boxOffsetX ();
450 dy = boxOffsetY ();
451 content.x = dx;
452 content.y = dy;
453 content.width = getContentWidth ();
454 content.height = getContentHeight ();
455
456 if (area->intersectsWith (&content, &intersection))
457 view->drawImage (buffer,
458 allocation.x + dx, allocation.y + dy,
459 intersection.x - dx, intersection.y - dy,
460 intersection.width, intersection.height);
461 } else {
462 core::View *clippingView;
463
464 if (altText && altText[0]) {
465 core::View *usedView = view;
466
467 clippingView = NULL;
468
469 if (altTextWidth == -1)
470 altTextWidth =
471 layout->textWidth (getStyle()->font, altText, strlen (altText));
472
473 if ((getContentWidth() < altTextWidth) ||
474 (getContentHeight() <
475 getStyle()->font->ascent + getStyle()->font->descent)) {
476 clippingView = usedView =
477 view->getClippingView (allocation.x + boxOffsetX (),
478 allocation.y + boxOffsetY (),
479 getContentWidth(),
480 getContentHeight());
481 }
482
483 usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color,
485 allocation.x + boxOffsetX (),
486 allocation.y + boxOffsetY (),
487 getContentWidth(), getContentHeight(), altText);
488
489 if (clippingView)
490 view->mergeClippingView (clippingView);
491 }
492 if (mapKey) {
493 clippingView = view->getClippingView (allocation.x + boxOffsetX (),
494 allocation.y + boxOffsetY (),
495 getContentWidth(),
496 getContentHeight());
497 mapList->drawMap(mapKey, clippingView, getStyle(),
498 allocation.x + boxOffsetX (),
499 allocation.y + boxOffsetY ());
500 view->mergeClippingView (clippingView);
501 }
502 }
503
505}
506
508{
509 //return new core::TextIterator (this, mask, atEnd, altText);
511 return new core::EmptyIterator (this, mask, atEnd);
512}
513
514void Image::setBuffer (core::Imgbuf *buffer, bool resize)
515{
516 core::Imgbuf *oldBuf = this->buffer;
517
518 if (wasAllocated () && needsResize () &&
519 getContentWidth () > 0 && getContentHeight () > 0) {
520 // Don't create a new buffer for the transition from alt text to img,
521 // and only scale when both dimensions are known.
522
523 bufWidth = getContentWidth ();
524 bufHeight = getContentHeight ();
525 this->buffer = buffer->getScaledBuf (bufWidth, bufHeight);
526 } else {
527 this->buffer = buffer;
528 bufWidth = buffer->getRootWidth ();
529 bufHeight = buffer->getRootHeight ();
530 buffer->ref ();
531 }
532 queueResize (0, true);
533
534 if (bufWidth)
535 this->ratio = (float) bufWidth / (float) bufHeight;
536
537 DBG_OBJ_ASSOC_CHILD (this->buffer);
538 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
539 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
540
541 if (oldBuf)
542 oldBuf->unref ();
543}
544
545void Image::drawRow (int row)
546{
547 core::Rectangle area;
548
549 assert (buffer != NULL);
550
551 buffer->getRowArea (row, &area);
552 if (area.width && area.height)
553 queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width,
554 area.height);
555}
556
558{
559 // Nothing to do; images are always drawn line by line.
560}
561
563{
564 // Could display an error.
565}
566
567
572{
573 isMap = true;
574}
575
576
585{
586 mapList = list;
587 if (mapKey && mapKey != key)
588 delete mapKey;
589 mapKey = key;
590}
591
592} // namespace dw
void setDefaultLink(int link)
Definition image.hh:64
void add(core::Shape *shape, int link)
Definition image.cc:57
int link(int x, int y)
Definition image.cc:64
void draw(core::View *view, core::style::Style *style, int x, int y)
Definition image.cc:45
lout::container::typed::List< ShapeAndLink > * shapesAndLinks
Definition image.hh:56
Represents a list of client-side image maps.
Definition image.hh:44
void drawMap(lout::object::Object *key, core::View *view, core::style::Style *style, int x, int y)
Definition image.cc:124
ImageMap * currentMap
Definition image.hh:70
void addShapeToCurrentMap(core::Shape *shape, int link)
Add a shape to the current map-.
Definition image.cc:111
void startNewMap(lout::object::Object *key)
Start a new map and make it the current one.
Definition image.cc:99
lout::container::typed::HashTable< lout::object::Object, ImageMap > * imageMaps
Definition image.hh:69
void setCurrentMapDefaultLink(int link)
Set default link for current map-.
Definition image.cc:119
int link(lout::object::Object *key, int x, int y)
Definition image.cc:133
Image(const char *altText, bool markEmpty=false)
Definition image.cc:148
void setIsMap()
Sets image as server side image map.
Definition image.cc:571
void setUseMap(ImageMapsList *list, Object *key)
Sets image as client side image map.
Definition image.cc:584
core::Iterator * iterator(core::Content::Type mask, bool atEnd)
Return an iterator for this widget.
Definition image.cc:507
int contentY(core::MousePositionEvent *event)
Definition image.cc:376
void finish()
Called, when all image data has been retrieved.
Definition image.cc:557
void drawRow(int row)
Called, when data from a row is available and has been copied into the image buffer.
Definition image.cc:545
void sizeAllocateImpl(core::Allocation *allocation)
See Sizes of Dillo Widgets.
Definition image.cc:289
bool buttonReleaseImpl(core::EventButton *event)
Definition image.cc:426
void sizeRequestSimpl(core::Requisition *requisition)
Simple variant, to be implemented by widgets with sizes not depending on positions.
Definition image.cc:186
static int CLASS_ID
Definition image.hh:177
void containerSizeChangedForChildren()
Definition image.cc:334
bool buttonPressImpl(core::EventButton *event)
Definition image.cc:409
void fatal()
Called, when there are problems with the retrieval of image data.
Definition image.cc:562
int contentX(core::MousePositionEvent *event)
Definition image.cc:368
void getExtremesSimpl(core::Extremes *extremes)
Simple variant, to be implemented by widgets with extremes not depending on positions.
Definition image.cc:256
void setBuffer(core::Imgbuf *buffer, bool resize=false)
Called, when an image buffer is attached.
Definition image.cc:514
void enterNotifyImpl(core::EventCrossing *event)
Definition image.cc:341
void leaveNotifyImpl(core::EventCrossing *event)
Definition image.cc:352
bool motionNotifyImpl(core::EventMotion *event)
Definition image.cc:384
void draw(core::View *view, core::Rectangle *area, core::DrawingContext *context)
Area is given in widget coordinates.
Definition image.cc:440
Set at the top when drawing.
Definition types.hh:295
This implementation of dw::core::Iterator can be used by widgets with no contents.
Definition iterator.hh:97
Represents a button press or release event.
Definition events.hh:58
Represents a enter or leave notify event.
Definition events.hh:75
Represents a mouse motion event.
Definition events.hh:68
The platform independent interface for image buffers.
Definition imgbuf.hh:162
virtual int getRootWidth()=0
virtual void ref()=0
virtual Imgbuf * getScaledBuf(int width, int height)=0
virtual void unref()=0
virtual int getRootHeight()=0
Iterators are used to iterate through the contents of a widget.
Definition iterator.hh:20
Base class for all mouse events related to a specific position.
Definition events.hh:49
dw::core::Shape implementation for simple rectangles.
Definition types.hh:70
bool intersectsWith(Rectangle *otherRect, Rectangle *dest)
Return whether this rectangle and otherRect intersect.
Definition types.cc:53
Abstract interface for different shapes.
Definition types.hh:59
virtual bool isPointWithin(int x, int y)=0
virtual void draw(core::View *view, core::style::Style *style, int x, int y)=0
An interface to encapsulate platform dependent drawing.
Definition view.hh:17
virtual void mergeClippingView(View *clippingView)=0
virtual void drawImage(Imgbuf *imgbuf, int xRoot, int yRoot, int x, int y, int width, int height)=0
virtual void drawSimpleWrappedText(style::Font *font, style::Color *color, style::Color::Shading shading, int x, int y, int w, int h, const char *text)=0
virtual View * getClippingView(int x, int y, int width, int height)=0
void setVal(int val)
Definition style.hh:510
void setBorderStyle(BorderStyle val)
Definition style.hh:588
void setBorderColor(Color *val)
Definition style.hh:585
static Style * create(StyleAttrs *attrs)
Definition style.hh:629
This is the base class for many other classes, which defines very common virtual methods.
Definition object.hh:45
#define DEBUG_MSG(level,...)
Definition debug.hh:29
#define DBG_OBJ_ENTER0(aspect, prio, funname)
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_SET_NUM(var, val)
#define DBG_OBJ_MSG(aspect, prio, msg)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
#define DBG_OBJ_ASSOC_CHILD(child)
char * dStrdup(const char *s)
Definition dlib.c:77
static Layout * layout
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition style.hh:446
void splitHeightPreserveDescent(int height, int *ascent, int *descent)
Definition widget.cc:2258
Dw is in this namespace, or sub namespaces of this one.
T min(T a, T b)
Definition misc.hh:40
T max(T a, T b)
Definition misc.hh:41
The DilloImage data-structure and methods.
Represents the allocation, i.e.
Definition types.hh:164