Dillo v3.1.1-98-g318d1f14
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 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)
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
162 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
163 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
164}
165
167{
168 if (altText)
169 free(altText);
170 if (buffer)
171 buffer->unref ();
172 if (mapKey)
173 delete mapKey;
174
176}
177
179{
180 DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
181
182 DEBUG_MSG(1, "-- Image::sizeRequestSimpl() begins\n");
183
184 DEBUG_MSG(1, "Image::sizeRequestImpl border: w=%d h=%d\n",
185 boxDiffWidth(), boxDiffHeight());
186
187 /* First set a naive size based on the image properties if given */
188
189 if (buffer) {
190 requisition->width = buffer->getRootWidth ();
191 requisition->ascent = buffer->getRootHeight ();
192 requisition->descent = 0;
193 } else {
194 if (altText && altText[0]) {
195 if (altTextWidth == -1)
196 altTextWidth =
197 layout->textWidth (getStyle()->font, altText, strlen (altText));
198
199 requisition->width = altTextWidth;
200 requisition->ascent = getStyle()->font->ascent;
201 requisition->descent = getStyle()->font->descent;
202 } else {
203 requisition->width = 0;
204 requisition->ascent = 0;
205 requisition->descent = 0;
206 }
207 }
208
209 requisition->width += boxDiffWidth ();
210 requisition->ascent += boxOffsetY ();
211 requisition->descent += boxRestHeight ();
212
213 DEBUG_MSG(1, "Image: initial requisition (with border): w=%d, h=%d\n",
214 requisition->width, requisition->ascent + requisition->descent);
215
216 /* Then correct the size if needed, so it fits within the available space in
217 * the container widget. The correctRequisition() method will take into the
218 * account the preferred aspect ratio. */
219
220 correctRequisition (requisition, core::splitHeightPreserveDescent, true,
221 true);
222
223 DEBUG_MSG(1, "Image: corrected requisition: w=%d, h=%d\n",
224 requisition->width, requisition->ascent + requisition->descent);
225
226 DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
227 requisition->width, requisition->ascent, requisition->descent);
228 DBG_OBJ_LEAVE ();
229}
230
232{
233 int contentWidth;
234 if (buffer)
235 contentWidth = buffer->getRootWidth ();
236 else {
237 if (altText && altText[0]) {
238 if (altTextWidth == -1)
239 altTextWidth =
240 layout->textWidth (getStyle()->font, altText, strlen (altText));
241 contentWidth = altTextWidth;
242 } else
243 contentWidth = 0;
244 }
245
246 int width = contentWidth + boxDiffWidth ();
247
248 // With percentage width, the image may be narrower than the buffer.
249 extremes->minWidth =
250 core::style::isPerLength (getStyle()->width) ? boxDiffWidth () : width;
251
252 // (We ignore the same effect for the maximal width.)
253 extremes->maxWidth = width;
254
255 extremes->minWidthIntrinsic = extremes->minWidth;
256 extremes->maxWidthIntrinsic = extremes->maxWidth;
257
258 correctExtremes (extremes, false);
259
260 extremes->adjustmentWidth =
261 misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
262}
263
265{
266 DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
267 allocation->x, allocation->y, allocation->width,
268 allocation->ascent, allocation->descent);
269
270 DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
271 allocation->x, allocation->y, allocation->width,
272 allocation->ascent, allocation->descent);
273
274 DEBUG_MSG(1, "Image::sizeAllocateImpl border: w=%d h=%d\n",
275 boxDiffWidth(), boxDiffHeight());
276
277
278 int newBufWidth = allocation->width - boxDiffWidth ();
279 int newBufHeight =
280 allocation->ascent + allocation->descent - boxDiffHeight ();
281
282 if (buffer && newBufWidth > 0 && newBufHeight > 0 &&
283 // Save some time when size did not change:
284 (newBufWidth != bufWidth || newBufHeight != bufHeight)) {
285 DBG_OBJ_MSG ("resize", 1, "replacing buffer");
286
287 DEBUG_MSG(1, "Image::sizeAllocateImpl new buffer size: w=%d h=%d\n",
288 newBufWidth, newBufHeight);
289
290 core::Imgbuf *oldBuffer = buffer;
291 buffer = oldBuffer->getScaledBuf (newBufWidth, newBufHeight);
292 oldBuffer->unref ();
293
294 bufWidth = newBufWidth;
295 bufHeight = newBufHeight;
296
297 DBG_OBJ_ASSOC_CHILD (this->buffer);
298 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
299 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
300 }
301
302 DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
303 allocation->x, allocation->y, allocation->width,
304 allocation->ascent, allocation->descent);
305
306 DBG_OBJ_LEAVE ();
307}
308
310{
311 DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
312 // Nothing to do.
313 DBG_OBJ_LEAVE ();
314}
315
317{
318 // BUG: this is wrong for image maps, but the cursor position is unknown.
319 currLink = getStyle()->x_link;
320
321 if (currLink != -1) {
322 (void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
323 }
324 Widget::enterNotifyImpl(event);
325}
326
328{
329 clicking = false;
330
331 if (currLink != -1) {
332 currLink = -1;
333 (void) layout->emitLinkEnter (this, -1, -1, -1, -1);
334 }
335 Widget::leaveNotifyImpl(event);
336}
337
338/*
339 * Return the coordinate relative to the contents.
340 * If the event occurred in the surrounding box, return the value at the
341 * edge of the contents instead.
342 */
344{
345 int ret = event->xWidget - boxOffsetX();
346
347 ret = misc::min(getContentWidth(), misc::max(ret, 0));
348 return ret;
349}
350
352{
353 int ret = event->yWidget - boxOffsetY();
354
355 ret = misc::min(getContentHeight(), misc::max(ret, 0));
356 return ret;
357}
358
360{
361 if (mapList || isMap) {
362 int x = contentX(event);
363 int y = contentY(event);
364
365 if (mapList) {
366 /* client-side image map */
367 int newLink = mapList->link (mapKey, x, y);
368 if (newLink != currLink) {
369 currLink = newLink;
370 clicking = false;
371 /* \todo Using MAP/AREA styles would probably be best */
372 setCursor(newLink == -1 ? getStyle()->cursor :
374 (void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
375 }
376 } else if (isMap && currLink != -1) {
377 /* server-side image map */
378 (void) layout->emitLinkEnter (this, currLink, -1, x, y);
379 }
380 }
381 return true;
382}
383
385{
386 bool ret = false;
387
388 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
389 getStyle()->x_link;
390 if (event->button == 3){
391 (void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
392 event);
393 ret = true;
394 } else if (event->button == 1 || currLink != -1){
395 clicking = true;
396 ret = true;
397 }
398 return ret;
399}
400
402{
403 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
404 getStyle()->x_link;
405 if (clicking) {
406 int x = isMap ? contentX(event) : -1;
407 int y = isMap ? contentY(event) : -1;
408 clicking = false;
409 layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
410 return true;
411 }
412 return false;
413}
414
416 core::DrawingContext *context)
417{
418 int dx, dy;
419 core::Rectangle content, intersection;
420
421 drawWidgetBox (view, area, false);
422
423 if (buffer) {
424 dx = boxOffsetX ();
425 dy = boxOffsetY ();
426 content.x = dx;
427 content.y = dy;
428 content.width = getContentWidth ();
429 content.height = getContentHeight ();
430
431 if (area->intersectsWith (&content, &intersection))
432 view->drawImage (buffer,
433 allocation.x + dx, allocation.y + dy,
434 intersection.x - dx, intersection.y - dy,
435 intersection.width, intersection.height);
436 } else {
437 core::View *clippingView;
438
439 if (altText && altText[0]) {
440 core::View *usedView = view;
441
442 clippingView = NULL;
443
444 if (altTextWidth == -1)
445 altTextWidth =
446 layout->textWidth (getStyle()->font, altText, strlen (altText));
447
448 if ((getContentWidth() < altTextWidth) ||
449 (getContentHeight() <
450 getStyle()->font->ascent + getStyle()->font->descent)) {
451 clippingView = usedView =
452 view->getClippingView (allocation.x + boxOffsetX (),
453 allocation.y + boxOffsetY (),
454 getContentWidth(),
455 getContentHeight());
456 }
457
458 usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color,
460 allocation.x + boxOffsetX (),
461 allocation.y + boxOffsetY (),
462 getContentWidth(), getContentHeight(), altText);
463
464 if (clippingView)
465 view->mergeClippingView (clippingView);
466 }
467 if (mapKey) {
468 clippingView = view->getClippingView (allocation.x + boxOffsetX (),
469 allocation.y + boxOffsetY (),
470 getContentWidth(),
471 getContentHeight());
472 mapList->drawMap(mapKey, clippingView, getStyle(),
473 allocation.x + boxOffsetX (),
474 allocation.y + boxOffsetY ());
475 view->mergeClippingView (clippingView);
476 }
477 }
478
480}
481
483{
484 //return new core::TextIterator (this, mask, atEnd, altText);
486 return new core::EmptyIterator (this, mask, atEnd);
487}
488
489void Image::setBuffer (core::Imgbuf *buffer, bool resize)
490{
491 core::Imgbuf *oldBuf = this->buffer;
492
493 if (wasAllocated () && needsResize () &&
494 getContentWidth () > 0 && getContentHeight () > 0) {
495 // Don't create a new buffer for the transition from alt text to img,
496 // and only scale when both dimensions are known.
497
498 bufWidth = getContentWidth ();
499 bufHeight = getContentHeight ();
500 this->buffer = buffer->getScaledBuf (bufWidth, bufHeight);
501 } else {
502 this->buffer = buffer;
503 bufWidth = buffer->getRootWidth ();
504 bufHeight = buffer->getRootHeight ();
505 buffer->ref ();
506 }
507 queueResize (0, true);
508
509 if (bufWidth)
510 this->ratio = (float) bufWidth / (float) bufHeight;
511
512 DBG_OBJ_ASSOC_CHILD (this->buffer);
513 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
514 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
515
516 if (oldBuf)
517 oldBuf->unref ();
518}
519
520void Image::drawRow (int row)
521{
522 core::Rectangle area;
523
524 assert (buffer != NULL);
525
526 buffer->getRowArea (row, &area);
527 if (area.width && area.height)
528 queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width,
529 area.height);
530}
531
533{
534 // Nothing to do; images are always drawn line by line.
535}
536
538{
539 // Could display an error.
540}
541
542
547{
548 isMap = true;
549}
550
551
560{
561 mapList = list;
562 if (mapKey && mapKey != key)
563 delete mapKey;
564 mapKey = key;
565}
566
567} // namespace dw
void setDefaultLink(int link)
Definition image.hh:44
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:36
Represents a list of client-side image maps.
Definition image.hh:24
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:50
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:49
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
void setIsMap()
Sets image as server side image map.
Definition image.cc:546
void setUseMap(ImageMapsList *list, Object *key)
Sets image as client side image map.
Definition image.cc:559
core::Iterator * iterator(core::Content::Type mask, bool atEnd)
Return an iterator for this widget.
Definition image.cc:482
int contentY(core::MousePositionEvent *event)
Definition image.cc:351
void finish()
Called, when all image data has been retrieved.
Definition image.cc:532
void drawRow(int row)
Called, when data from a row is available and has been copied into the image buffer.
Definition image.cc:520
Image(const char *altText)
Definition image.cc:148
void sizeAllocateImpl(core::Allocation *allocation)
See Sizes of Dillo Widgets.
Definition image.cc:264
bool buttonReleaseImpl(core::EventButton *event)
Definition image.cc:401
void sizeRequestSimpl(core::Requisition *requisition)
Simple variant, to be implemented by widgets with sizes not depending on positions.
Definition image.cc:178
static int CLASS_ID
Definition image.hh:152
void containerSizeChangedForChildren()
Definition image.cc:309
bool buttonPressImpl(core::EventButton *event)
Definition image.cc:384
void fatal()
Called, when there are problems with the retrieval of image data.
Definition image.cc:537
int contentX(core::MousePositionEvent *event)
Definition image.cc:343
void getExtremesSimpl(core::Extremes *extremes)
Simple variant, to be implemented by widgets with extremes not depending on positions.
Definition image.cc:231
void setBuffer(core::Imgbuf *buffer, bool resize=false)
Called, when an image buffer is attached.
Definition image.cc:489
void enterNotifyImpl(core::EventCrossing *event)
Definition image.cc:316
void leaveNotifyImpl(core::EventCrossing *event)
Definition image.cc:327
bool motionNotifyImpl(core::EventMotion *event)
Definition image.cc:359
void draw(core::View *view, core::Rectangle *area, core::DrawingContext *context)
Area is given in widget coordinates.
Definition image.cc:415
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 implemtation 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
This is the base class for many other classes, which defines very common virtual methods.
Definition object.hh:25
#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