diff options
39 files changed, 2165 insertions, 305 deletions
@@ -27,6 +27,7 @@ ^test/dw-border-test$ ^test/dw-example$ ^test/dw-find-test$ +^test/dw-image-background$ ^test/dw-images-scaled$ ^test/dw-images-scaled2$ ^test/dw-images-simple$ diff --git a/doc/dw-images-and-backgrounds.doc b/doc/dw-images-and-backgrounds.doc index 356652a6..caa08e8f 100644 --- a/doc/dw-images-and-backgrounds.doc +++ b/doc/dw-images-and-backgrounds.doc @@ -1,10 +1,11 @@ /** \page dw-images-and-backgrounds Images and Backgrounds in Dw -<h2>General</h2> +Image Buffers +============= -Representation of the image data is delegated to dw::core::Imgbuf, see -there for details. Drawing is delegated to dw::core::View -(dw::core::View::drawImgbuf). +Representation of the image data is done by dw::core::Imgbuf, see +there for details. Drawing is done by dw::core::View +(dw::core::View::drawImage). Since dw::core::Imgbuf provides memory management based on reference counting, there may be an 1-to-n relation from image renderers (image @@ -15,61 +16,73 @@ dw::core::Imgbuf::copyRow) notify all renderers connected to the buffer. -<h2>Images</h2> +Image Renderer +============== + +Generally, there are no restrictions on how to manage +dw::core::Imgbuf; but to handle image data from web resources, the +interface dw::core::ImgRenderer should be implemented. It is again +wrapped by DilloImago (to make access from the C part possible, since +dw::core::ImgRenderer is written in C++), which is referenced by +DilloWeb. There are two positions where retrieving image data is +initiated: + +- Html_load_image: for embedded images (implemented by dw::Image, + which implements dw::core::ImgRenderer); +- StyleEngine::apply (search for "case + CSS_PROPERTY_BACKGROUND_IMAGE"): for backgrond images; there are + some implementations of dw::core::ImgRenderer within the context of + dw::core::style::StyleImage. + +Both are described in detail below. Notice that the code is quite +similar; only the implementation of dw::core::ImgRenderer differs. + +At this time, dw::core::ImgRenderer has got two methods (see more +documentation there): + +- dw::core::ImgRenderer::setBuffer, +- dw::core::ImgRenderer::drawRow, +- dw::core::ImgRenderer::finish, and +- dw::core::ImgRenderer::fatal. + + +Images +====== This is the simplest renderer, displaying an image. For each row to be drawn, -<ol> -<li> first dw::core::Imgbuf::copyRow, and then -<li> for each dw::Image, dw::Image::drawRow must be called, with the same - argument (no scaling is necessary). -</ol> +- first dw::core::Imgbuf::copyRow, and then +- for each dw::Image, dw::Image::drawRow must be called, with the same + argument (no scaling is necessary). dw::Image automatically scales the dw::core::Imgbuf, the root buffer should be passed to dw::Image::setBuffer. \see dw::Image for more details. -<h2>Future Extensions</h2> -(This is not implemented yet.) Rendering itself (image widgets and -background) will be abstracted, by a new interface -dw::core::ImageRenderer. In the current code for image decoding, this -interface will replace references to dw::Image, which implements -dw::core::ImageRenderer, in most cases. +Background Images +================= -<h2>Backgrounds</h2> +Since background images are style resources, they are associated with +dw::core::style::Style, as dw::core::style::StyleImage, which is +handled in a similar way (reference counting etc.) as +dw::core::style::Color and dw::core::style::Font, although it is +concrete and not platform-dependant. -(This is based on future extensions described above.) Since background -are style resources, they are associated with -dw::core::style::Style. For backgrounds, another level is needed, -because of the 1-to-n relation from dw::core::style::Style to -dw::core::Widget: +The actual renderer (passed to Web) is an instance of +dw::core::ImgRendererDist (distributes all calls to a set of other +instances of dw::core::ImgRenderer), which contains two kinds of +renderers: -\dot -digraph G { - node [shape=record, fontname=Helvetica, fontsize=10]; - edge [arrowhead="open", dir="both", arrowtail="none", - labelfontname=Helvetica, labelfontsize=10, color="#404040", - labelfontcolor="#000080"]; - fontname=Helvetica; fontsize=10; - - "background renderer (as a part of style)" -> "image buffer" [headlabel="*", - taillabel="1"]; - "widget (or a part of it)" -> "background renderer (as a part of style)" - [headlabel="*", taillabel="1"]; -} -\enddot - -Unlike dw::Image, dw::core::style::BgRenderer is not associated with a -certain rectangle on the canvas. Instead, widgets, or parts of widgets -take this role. This is generally represented by an implementation of -the interface dw::core::style::BgAllocation, which is implemented by -dw::core::Widget, but also by all parts of widget implementation, -which may have an own background image. +- one instance of dw::core::style::StyleImage::StyleImgRenderer, which + does everything needed for dw::core::style::StyleImage, and +- other renderes, used externally (widgets etc.), which are added by + dw::core::style::StyleImage::putExternalImgRenderer (and removed by + dw::core::style::StyleImage::removeExternalImgRenderer). -The following diagram gives a total overview: +This diagram gives an comprehensive overview: \dot digraph G { @@ -79,70 +92,151 @@ digraph G { labelfontcolor="#000080"]; fontname=Helvetica; fontsize=10; - "DICache Entry"; - - subgraph cluster_dw_images { + subgraph cluster_dw_style { style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; - label="Dw Images"; - ImageRenderer [URL="\ref dw::core::ImageRenderer", color="#ff8080"]; - Imgbuf [URL="\ref dw::core::Imgbuf", color="#ff8080"]; + Style [URL="\ref dw::core::style::Style"]; + StyleImage [URL="\ref dw::core::style::StyleImage"]; + Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"]; + StyleImgRenderer + [URL="\ref dw::core::style::StyleImage::StyleImgRenderer"]; + ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"]; + ImgRendererDist [URL="\ref dw::core::ImgRendererDist"]; + ExternalImgRenderer + [URL="\ref dw::core::style::StyleImage::ExternalImgRenderer", + color="#a0a0a0"]; + ExternalWidgetImgRenderer + [URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer", + color="#a0a0a0"]; } - subgraph cluster_widgets { + subgraph cluster_dw_layout { style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; - label="Widgets"; - Widget [URL="\ref dw::core::Widget", color="#a0a0a0"]; - Textblock [URL="\ref dw::Textblock"]; - "Textblock::Word" [URL="\ref dw::Textblock::Word"]; - Table [URL="\ref dw::Table"]; - "Table::Row" [URL="\ref dw::Table::Row"]; - Image [URL="\ref dw::Image"]; + Layout [URL="\ref dw::core::Layout"]; + LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"]; } - subgraph cluster_style { + subgraph cluster_dw_widget { style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; - label="dw::core::style"; - Style [URL="\ref dw::core::style::Style"]; - BgRenderer [URL="\ref dw::core::style::BgRenderer"]; - BgAllocation [URL="\ref dw::core::style::BgAllocation", color="#ff8080"]; + Widget [URL="\ref dw::core::Widget", color="#a0a0a0"]; + WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"]; } - "DICache Entry" -> ImageRenderer [headlabel="*", taillabel="1"]; - "DICache Entry" -> Imgbuf [headlabel="1", taillabel="1"]; - - BgRenderer -> Imgbuf [headlabel="1", taillabel="*"]; - BgRenderer -> BgAllocation [headlabel="*", taillabel="1"]; - ImageRenderer -> BgRenderer [arrowhead="none", arrowtail="empty", dir="both", - style="dashed"]; - ImageRenderer -> Image [arrowhead="none", arrowtail="empty", dir="both", - style="dashed"]; - - Style -> BgRenderer [headlabel="0..1", taillabel="1"]; + subgraph cluster_dw_textblock { + style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10; - Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"]; - Textblock -> "Textblock::Word" [headlabel="*", taillabel="1"]; - Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"]; - Table -> "Table::Row" [headlabel="*", taillabel="1"]; - Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"]; + Textblock [URL="\ref dw::Textblock"]; + Word [URL="\ref dw::Textblock::Word"]; + WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"]; + SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"]; + } - BgAllocation -> Widget [arrowhead="none", arrowtail="empty", dir="both", - style="dashed"]; - BgAllocation -> "Textblock::Word" [arrowhead="none", arrowtail="empty", + Style -> StyleImage [headlabel="*", taillabel="1"]; + StyleImage -> Imgbuf [headlabel="*", taillabel="1"]; + StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"]; + StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"]; + ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"]; + ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"]; + + ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; + ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; + ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty", dir="both", style="dashed"]; - BgAllocation -> "Table::Row" [arrowhead="none", arrowtail="empty", - dir="both", style="dashed"]; + ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + + Textblock -> Word [headlabel="1", taillabel="*"]; + Word -> WordImgRenderer [headlabel="1", taillabel="0..1"]; + Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"]; + ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none", + arrowtail="empty", dir="both", style="dashed"]; + WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty", + dir="both", style="dashed"]; } \enddot <center>[\ref uml-legend "legend"]</center> -<h2>Integration into dillo</h2> +Memory management +----------------- + +dw::core::style::StyleImage extends lout::signal::ObservedObject, so +that deleting this instance can be connected to code dealing with +cache clients etc. See StyleImageDeletionReceiver and how it is +attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE"). + + +Bugs and Things Needing Improvement +=================================== + +(Mostly related to image backgrounds, when not otherwise mentioned.) + +High Priority +------------- + +**Images as toplevel resource:** There seem to be problems when the +toplevel resource is an image. How to reproduce: Type <code>src/dillo +http://www.dillo.org/db1.png</code> with the version from the +background images branch; this will print the message "Content-Type +'image/png' not viewable." and show the "Save link as file" +dialog. The same works for the version from the main repository, with +which the background images branch has been synchronized. + +**Configurability, security/privacy aspects, etc.,** which are +currently available for image widgets, should be adopted. Perhaps some +more configuration options specially for background images. + + +Medium Priority +--------------- + +**Background-attachment** is not yet implemented, and will be postponed. + +**Small background images** result in a large number of calls to +dw::core::View::drawImage, which may become slow. Solution: Create a +new image buffer, which contains several copies of the original image. + +**Drawing background images row by row** may become slow. As an +alternative, dw::core::ImgRenderer::finish could be used. However, +drawing row by row could become an option. **Update:** There is now +dw::core::style::drawBackgroundLineByLine, which can be changed in the +code. + +(For image widgets, this could also become an option: in contexts, +when image data is retrieved in a very fast way.) + +**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an +example, it is not called, when connecting to a server fails. (And so, +as far as I see, no cache client is started.) + + +Low Priority +------------ + +**Alpha support:** (not related to image backgrounds) currently alpha +support (and also colormap management) is done in dicache, while +dw::Image is only created with type RGB. This leads to several problems: -\todo Add some references. +- One dicache entry (representing an image related to the same URL), + which has only one background color, may refer to different images + with different background colors. +- The dicache only handles background colors, not background images. +The solution is basicly simple: keep out alpha support out of dicache; +instead implement RGBA in dw::Image. As it seems, the main problem is +alpha support in FLTK/X11. */ diff --git a/dw/Makefile.am b/dw/Makefile.am index e108da60..d0d56d2a 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -14,6 +14,8 @@ libDw_core_a_SOURCES = \ findtext.cc \ findtext.hh \ imgbuf.hh \ + imgrenderer.hh \ + imgrenderer.cc \ iterator.cc \ iterator.hh \ layout.cc \ @@ -44,6 +44,7 @@ class ResourceFactory; #include "types.hh" #include "events.hh" #include "imgbuf.hh" +#include "imgrenderer.hh" #include "style.hh" #include "view.hh" #include "platform.hh" diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc index 81d2dc2d..e526ad60 100644 --- a/dw/fltkimgbuf.cc +++ b/dw/fltkimgbuf.cc @@ -427,6 +427,37 @@ int FltkImgbuf::getRootHeight () return root ? root->height : height; } +core::Imgbuf *FltkImgbuf::createSimilarBuf (int width, int height) +{ + return new FltkImgbuf (type, width, height, gamma); +} + +void FltkImgbuf::copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc) +{ + FltkImgbuf *fDest = (FltkImgbuf*)dest; + assert (bpp == fDest->bpp); + + int xSrc2 = lout::misc::min (xSrc + widthSrc, fDest->width - xDestRoot); + int ySrc2 = lout::misc::min (ySrc + heightSrc, fDest->height - yDestRoot); + + //printf ("copying from (%d, %d), %d x %d to (%d, %d) (root) => " + // "xSrc2 = %d, ySrc2 = %d\n", + // xSrc, ySrc, widthSrc, heightSrc, xDestRoot, yDestRoot, + // xSrc2, ySrc2); + + for (int x = xSrc; x < xSrc2; x++) + for (int y = ySrc; y < ySrc2; y++) { + int iSrc = x + width * y; + int iDest = xDestRoot + x + fDest->width * (yDestRoot + y); + + //printf (" (%d, %d): %d -> %d\n", x, y, iSrc, iDest); + + for (int b = 0; b < bpp; b++) + fDest->rawdata[bpp * iDest + b] = rawdata[bpp * iSrc + b]; + } +} + void FltkImgbuf::ref () { refCount++; diff --git a/dw/fltkimgbuf.hh b/dw/fltkimgbuf.hh index ee5ae922..0b8b5549 100644 --- a/dw/fltkimgbuf.hh +++ b/dw/fltkimgbuf.hh @@ -73,6 +73,9 @@ public: void getRowArea (int row, dw::core::Rectangle *area); int getRootWidth (); int getRootHeight (); + core::Imgbuf *createSimilarBuf (int width, int height); + void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc); void ref (); void unref (); diff --git a/dw/image.cc b/dw/image.cc index 1cb9ce39..acb2779e 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -450,6 +450,16 @@ void Image::drawRow (int row) area.width, area.height); } +void Image::finish () +{ + // Nothing to do; images are always drawn line by line. +} + +void Image::fatal () +{ + // Could display an error. +} + /** * \brief Sets image as server side image map. diff --git a/dw/image.hh b/dw/image.hh index a8314d4f..a712936e 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -116,7 +116,7 @@ public: * * \sa\ref dw-images-and-backgrounds */ -class Image: public core::Widget +class Image: public core::Widget, public core::ImgRenderer { private: char *altText; @@ -157,6 +157,9 @@ public: void drawRow (int row); + void finish (); + void fatal (); + void setIsMap (); void setUseMap (ImageMapsList *list, Object *key); diff --git a/dw/imgbuf.hh b/dw/imgbuf.hh index 02ba9087..3ccbe3c5 100644 --- a/dw/imgbuf.hh +++ b/dw/imgbuf.hh @@ -178,6 +178,19 @@ public: virtual int getRootWidth () = 0; virtual int getRootHeight () = 0; + + /** + * Creates an image buffer with same parameters (type, gamma etc.) + * except size. + */ + virtual Imgbuf *createSimilarBuf (int width, int height) = 0; + + /** + * Copies another image buffer into this image buffer. + */ + virtual void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot, + int xSrc, int ySrc, int widthSrc, int heightSrc) = 0; + /* * Reference counting. */ diff --git a/dw/imgrenderer.cc b/dw/imgrenderer.cc new file mode 100644 index 00000000..285a8dcd --- /dev/null +++ b/dw/imgrenderer.cc @@ -0,0 +1,48 @@ +#include "core.hh" + +namespace dw { +namespace core { + +using namespace lout::container; +using namespace lout::object; + +void ImgRendererDist::setBuffer (core::Imgbuf *buffer, bool resize) +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->setBuffer (buffer, resize); + } +} + +void ImgRendererDist::drawRow (int row) +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->drawRow (row); + } +} + + +void ImgRendererDist::finish () +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->finish (); + } +} + +void ImgRendererDist::fatal () +{ + for (typed::Iterator <TypedPointer <ImgRenderer> > it = + children->iterator (); it.hasNext (); ) { + TypedPointer <ImgRenderer> *tp = it.getNext (); + tp->getTypedValue()->fatal (); + } +} + + +} // namespace core +} // namespace dw diff --git a/dw/imgrenderer.hh b/dw/imgrenderer.hh new file mode 100644 index 00000000..325a1998 --- /dev/null +++ b/dw/imgrenderer.hh @@ -0,0 +1,87 @@ +#ifndef __DW_IMGRENDERER_HH__ +#define __DW_IMGRENDERER_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +namespace dw { +namespace core { + +/** + * \brief ... + * + * \sa \ref dw-images-and-backgrounds + */ +class ImgRenderer +{ +public: + virtual ~ImgRenderer () { } + + /** + * \brief Called, when an image buffer is attached. + * + * This is typically the case when all meta data (size, depth) has been read. + */ + virtual void setBuffer (core::Imgbuf *buffer, bool resize = false) = 0; + + /** + * \brief Called, when data from a row is available and has been copied into + * the image buffer. + * + * The implementation will typically queue the respective area for drawing. + */ + virtual void drawRow (int row) = 0; + + /** + * \brief Called, when all image data has been retrieved. + * + * The implementation may use this instead of "drawRow" for drawing, to + * limit the number of draws. + */ + virtual void finish () = 0; + + /** + * \brief Called, when there are problems with the retrieval of image data. + * + * The implementation may use this to indicate an error. + */ + virtual void fatal () = 0; +}; + +/** + * \brief Implementation of ImgRenderer, which distributes all calls + * to a set of other implementations of ImgRenderer. + * + * The order of the call children is not defined, especially not + * identical to the order in which they have been added. + */ +class ImgRendererDist: public ImgRenderer +{ + lout::container::typed::HashSet <lout::object::TypedPointer <ImgRenderer> > + *children; + +public: + inline ImgRendererDist () + { children = new lout::container::typed::HashSet + <lout::object::TypedPointer <ImgRenderer> > (true); } + ~ImgRendererDist () { delete children; } + + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + + void put (ImgRenderer *child) + { children->put (new lout::object::TypedPointer <ImgRenderer> (child)); } + void remove (ImgRenderer *child) + { lout::object::TypedPointer <ImgRenderer> tp (child); + children->remove (&tp); } +}; + +} // namespace core +} // namespace dw + +#endif // __DW_IMGRENDERER_HH__ + + diff --git a/dw/layout.cc b/dw/layout.cc index 3f493d8a..89361881 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -32,6 +32,65 @@ using namespace lout::object; namespace dw { namespace core { +bool Layout::LayoutImgRenderer::readyToDraw () +{ + return true; +} + +void Layout::LayoutImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + // TODO Actually not padding area, but visible area? + getRefArea (x, y, width, height); +} + +void Layout::LayoutImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) +{ + *xRef = 0; + *yRef = 0; + *widthRef = misc::max (layout->viewportWidth + - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0), + layout->canvasWidth); + *heightRef = misc::max (layout->viewportHeight + - layout->hScrollbarThickness, + layout->canvasAscent + layout->canvasDescent); +} + +style::StyleImage *Layout::LayoutImgRenderer::getBackgroundImage () +{ + return layout->bgImage; +} + +style::BackgroundRepeat Layout::LayoutImgRenderer::getBackgroundRepeat () +{ + return layout->bgRepeat; +} + +style::BackgroundAttachment + Layout::LayoutImgRenderer::getBackgroundAttachment () +{ + return layout->bgAttachment; +} + +style::Length Layout::LayoutImgRenderer::getBackgroundPositionX () +{ + return layout->bgPositionX; +} + +style::Length Layout::LayoutImgRenderer::getBackgroundPositionY () +{ + return layout->bgPositionY; +} + +void Layout::LayoutImgRenderer::draw (int x, int y, int width, int height) +{ + layout->queueDraw (x, y, width, height); +} + +// ---------------------------------------------------------------------- + void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent) { } @@ -189,6 +248,7 @@ Layout::Layout (Platform *platform) DBG_OBJ_CREATE (this, "DwRenderLayout"); bgColor = NULL; + bgImage = NULL; cursor = style::CURSOR_DEFAULT; canvasWidth = canvasAscent = canvasDescent = 0; @@ -216,18 +276,28 @@ Layout::Layout (Platform *platform) platform->setLayout (this); selectionState.setLayout(this); + + layoutImgRenderer = NULL; } Layout::~Layout () { widgetAtPoint = NULL; + if (layoutImgRenderer) { + if (bgImage) + bgImage->removeExternalImgRenderer (layoutImgRenderer); + delete layoutImgRenderer; + } + if (scrollIdleId != -1) platform->removeIdle (scrollIdleId); if (resizeIdleId != -1) platform->removeIdle (resizeIdleId); if (bgColor) bgColor->unref (); + if (bgImage) + bgImage->unref (); if (topLevel) { Widget *w = topLevel; topLevel = NULL; @@ -508,6 +578,23 @@ void Layout::draw (View *view, Rectangle *area) { Rectangle widgetArea, intersection, widgetDrawArea; + // First of all, draw background image. (Unlike background *color*, + // this is not a feature of the views.) + if (bgImage != NULL && bgImage->getImgbufSrc() != NULL) + style::drawBackgroundImage (view, bgImage, bgRepeat, bgAttachment, + bgPositionX, bgPositionY, + area->x, area->y, area->width, + area->height, 0, 0, + // Reference area: maximum of canvas size and + // viewport size. + misc::max (viewportWidth + - (canvasHeightGreater ? + vScrollbarThickness : 0), + canvasWidth), + misc::max (viewportHeight + - hScrollbarThickness, + canvasAscent + canvasDescent)); + if (scrollIdleId != -1) { /* scroll is pending, defer draw until after scrollIdle() */ drawAfterScrollReq = true; @@ -650,6 +737,37 @@ void Layout::setBgColor (style::Color *color) view->setBgColor (bgColor); } +void Layout::setBgImage (style::StyleImage *bgImage, + style::BackgroundRepeat bgRepeat, + style::BackgroundAttachment bgAttachment, + style::Length bgPositionX, style::Length bgPositionY) +{ + if (layoutImgRenderer && this->bgImage) + this->bgImage->removeExternalImgRenderer (layoutImgRenderer); + + if (bgImage) + bgImage->ref (); + + if (this->bgImage) + this->bgImage->unref (); + + this->bgImage = bgImage; + this->bgRepeat = bgRepeat; + this->bgAttachment = bgAttachment; + this->bgPositionX = bgPositionX; + this->bgPositionY = bgPositionY; + + if (bgImage) { + // Create instance of LayoutImgRenderer when needed. Until this + // layout is deleted, "layoutImgRenderer" will be kept, since it + // is not specific to the style, but only to this layout. + if (layoutImgRenderer == NULL) + layoutImgRenderer = new LayoutImgRenderer (this); + bgImage->putExternalImgRenderer (layoutImgRenderer); + } +} + + void Layout::resizeIdle () { //static int calls = 0; diff --git a/dw/layout.hh b/dw/layout.hh index 51d764a4..47554b42 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -17,6 +17,27 @@ class Layout: public lout::object::Object { friend class Widget; +private: + class LayoutImgRenderer: public style::StyleImage::ExternalImgRenderer + { + Layout *layout; + + public: + LayoutImgRenderer (Layout *layout) { this->layout = layout; } + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + style::StyleImage *getBackgroundImage (); + style::BackgroundRepeat getBackgroundRepeat (); + style::BackgroundAttachment getBackgroundAttachment (); + style::Length getBackgroundPositionX (); + style::Length getBackgroundPositionY (); + void draw (int x, int y, int width, int height); + }; + + LayoutImgRenderer *layoutImgRenderer; + public: /** * \brief Receiver interface different signals. @@ -135,6 +156,11 @@ private: /* The state, which must be projected into the view. */ style::Color *bgColor; + style::StyleImage *bgImage; + style::BackgroundRepeat bgRepeat; + style::BackgroundAttachment bgAttachment; + style::Length bgPositionX, bgPositionY; + style::Cursor cursor; int canvasWidth, canvasAscent, canvasDescent; @@ -386,8 +412,13 @@ public: inline void resetSearch () { findtextState.resetSearch (); } void setBgColor (style::Color *color); + void setBgImage (style::StyleImage *bgImage, + style::BackgroundRepeat bgRepeat, + style::BackgroundAttachment bgAttachment, + style::Length bgPositionX, style::Length bgPositionY); inline style::Color* getBgColor () { return bgColor; } + inline style::StyleImage* getBgImage () { return bgImage; } }; } // namespace core diff --git a/dw/style.cc b/dw/style.cc index 6c0abda2..3b57ffcf 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -34,6 +34,27 @@ namespace dw { namespace core { namespace style { +const bool drawBackgroundLineByLine = true; + +const int MIN_BG_IMG_W = 10; +const int MIN_BG_IMG_H = 10; +const int OPT_BG_IMG_W = 50; +const int OPT_BG_IMG_H = 50; + +static void calcBackgroundRelatedValues (StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment + backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int xDraw, int yDraw, int widthDraw, + int heightDraw, int xRef, int yRef, + int widthRef, int heightRef, + bool *repeatX, bool *repeatY, + int *origX, int *origY, + int *tileX1, int *tileX2, int *tileY1, + int *tileY2, bool *doDraw); + void StyleAttrs::initValues () { x_link = -1; @@ -48,6 +69,11 @@ void StyleAttrs::initValues () listStyleType = LIST_STYLE_TYPE_DISC; valign = VALIGN_BASELINE; backgroundColor = NULL; + backgroundImage = NULL; + backgroundRepeat = BACKGROUND_REPEAT; + backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL; + backgroundPositionX = createPerLength (0); + backgroundPositionY = createPerLength (0); width = height = lineHeight = LENGTH_AUTO; textIndent = 0; margin.setVal (0); @@ -76,6 +102,11 @@ void StyleAttrs::resetValues () valign = VALIGN_BASELINE; textAlignChar = '.'; backgroundColor = NULL; + backgroundImage = NULL; + backgroundRepeat = BACKGROUND_REPEAT; + backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL; + backgroundPositionX = createPerLength (0); + backgroundPositionY = createPerLength (0); width = LENGTH_AUTO; height = LENGTH_AUTO; @@ -116,6 +147,11 @@ bool StyleAttrs::equals (object::Object *other) { textDecoration == otherAttrs->textDecoration && color == otherAttrs->color && backgroundColor == otherAttrs->backgroundColor && + backgroundImage == otherAttrs->backgroundImage && + backgroundRepeat == otherAttrs->backgroundRepeat && + backgroundAttachment == otherAttrs->backgroundAttachment && + backgroundPositionX == otherAttrs->backgroundPositionX && + backgroundPositionY == otherAttrs->backgroundPositionY && textAlign == otherAttrs->textAlign && valign == otherAttrs->valign && textAlignChar == otherAttrs->textAlignChar && @@ -156,6 +192,11 @@ int StyleAttrs::hashValue () { textDecoration + (intptr_t) color + (intptr_t) backgroundColor + + (intptr_t) backgroundImage + + backgroundRepeat + + backgroundAttachment + + backgroundPositionX + + backgroundPositionY + textAlign + valign + textAlignChar + @@ -205,6 +246,8 @@ Style::Style (StyleAttrs *attrs) color->ref (); if (backgroundColor) backgroundColor->ref (); + if (backgroundImage) + backgroundImage->ref (); if (borderColor.top) borderColor.top->ref(); if (borderColor.bottom) @@ -227,6 +270,8 @@ Style::~Style () color->unref (); if (backgroundColor) backgroundColor->unref (); + if (backgroundImage) + backgroundImage->unref (); if (borderColor.top) borderColor.top->unref(); if (borderColor.bottom) @@ -248,6 +293,11 @@ void Style::copyAttrs (StyleAttrs *attrs) textDecoration = attrs->textDecoration; color = attrs->color; backgroundColor = attrs->backgroundColor; + backgroundImage = attrs->backgroundImage; + backgroundRepeat = attrs->backgroundRepeat; + backgroundAttachment = attrs->backgroundAttachment; + backgroundPositionX = attrs->backgroundPositionX; + backgroundPositionY = attrs->backgroundPositionY; textAlign = attrs->textAlign; valign = attrs->valign; textAlignChar = attrs->textAlignChar; @@ -422,6 +472,205 @@ Tooltip *Tooltip::create (Layout *layout, const char *text) // ---------------------------------------------------------------------- +void StyleImage::StyleImgRenderer::setBuffer (core::Imgbuf *buffer, bool resize) +{ + if (image->imgbufSrc) + image->imgbufSrc->unref (); + if (image->imgbufTiled) + image->imgbufTiled->unref (); + + image->imgbufTiled = NULL; + + image->imgbufSrc = buffer; + if (image->imgbufSrc) { + image->imgbufSrc->ref (); + + // If the image is too small, drawing a background will cause + // many calls of View::drawImgbuf. For this reason, we create + // another image buffer, the "tiled" image buffer, which is + // larger (the "optimal" size is defined as OPT_BG_IMG_W * + // OPT_BG_IMG_H) and contains the "source" buffer several times. + + if (image->imgbufSrc->getRootWidth() * image->imgbufSrc->getRootHeight() + < MIN_BG_IMG_W * MIN_BG_IMG_H) { + image->tilesX = + misc::max (OPT_BG_IMG_W / image->imgbufSrc->getRootWidth(), 1); + image->tilesY = + misc::max (OPT_BG_IMG_H / image->imgbufSrc->getRootHeight(), 1); + image->imgbufTiled = + image->imgbufSrc->createSimilarBuf + (image->tilesX * image->imgbufSrc->getRootWidth(), + image->tilesY * image->imgbufSrc->getRootHeight()); + } + } +} + +void StyleImage::StyleImgRenderer::drawRow (int row) +{ + if (image->imgbufTiled) { + // A row of data has been copied to the source buffer, here it + // is copied into the tiled buffer. + + // Unfortunately, this code may be called *after* some other + // implementations of ImgRenderer::drawRow, which actually + // *draw* the tiled buffer, which is so not up to date + // (ImgRendererDist does not define an order). OTOH, these + // drawing implementations calle Widget::queueResize, so the + // actual drawing (and so access to the tiled buffer) is done + // later. + + int w = image->imgbufSrc->getRootWidth (); + int h = image->imgbufSrc->getRootHeight (); + + for (int x = 0; x < image->tilesX; x++) + for (int y = 0; y < image->tilesX; y++) + image->imgbufSrc->copyTo (image->imgbufTiled, x * w, y * h, + 0, row, w, 1); + } +} + +void StyleImage::StyleImgRenderer::finish () +{ + // Nothing to do. +} + +void StyleImage::StyleImgRenderer::fatal () +{ + // Nothing to do. +} + +StyleImage::StyleImage () +{ + //printf ("new StyleImage %p\n", this); + + refCount = 0; + imgbufSrc = NULL; + imgbufTiled = NULL; + + imgRendererDist = new ImgRendererDist (); + styleImgRenderer = new StyleImgRenderer (this); + imgRendererDist->put (styleImgRenderer); +} + +StyleImage::~StyleImage () +{ + //printf ("delete StyleImage %p\n", this); + + if (imgbufSrc) + imgbufSrc->unref (); + if (imgbufTiled) + imgbufTiled->unref (); + + delete imgRendererDist; + delete styleImgRenderer; +} + +void StyleImage::ExternalImgRenderer::setBuffer (core::Imgbuf *buffer, + bool resize) +{ + // Nothing to do? +} + +void StyleImage::ExternalImgRenderer::drawRow (int row) +{ + if (drawBackgroundLineByLine) { + StyleImage *backgroundImage; + if (readyToDraw () && (backgroundImage = getBackgroundImage ())) { + // All single rows are drawn. + + Imgbuf *imgbuf = backgroundImage->getImgbufSrc(); + int imgWidth = imgbuf->getRootWidth (); + int imgHeight = imgbuf->getRootHeight (); + + int x, y, width, height; + getBgArea (&x, &y, &width, &height); + + int xRef, yRef, widthRef, heightRef; + getRefArea (&xRef, &yRef, &widthRef, &heightRef); + + bool repeatX, repeatY, doDraw; + int origX, origY, tileX1, tileX2, tileY1, tileY2; + + calcBackgroundRelatedValues (backgroundImage, + getBackgroundRepeat (), + getBackgroundAttachment (), + getBackgroundPositionX (), + getBackgroundPositionY (), + x, y, width, height, xRef, yRef, widthRef, + heightRef, &repeatX, &repeatY, &origX, + &origY, &tileX1, &tileX2, &tileY1, + &tileY2, &doDraw); + + //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n", + // tileX1, tileX2, tileY1, tileY2); + + if (doDraw) + // Only iterate over y, because the rows can be combined + // horizontically. + for (int tileY = tileY1; tileY <= tileY2; tileY++) { + int x1 = misc::max (origX + tileX1 * imgWidth, x); + int x2 = misc::min (origX + (tileX2 + 1) * imgWidth, x + width); + + int yt = origY + tileY * imgHeight + row; + if (yt >= y && yt < y + height) + draw (x1, yt, x2 - x1, 1); + } + } + } +} + +void StyleImage::ExternalImgRenderer::finish () +{ + if (!drawBackgroundLineByLine) { + if (readyToDraw ()) { + // Draw total area, as a whole. + int x, y, width, height; + getBgArea (&x, &y, &width, &height); + draw (x, y, width, height); + } + } +} + +void StyleImage::ExternalImgRenderer::fatal () +{ + // Nothing to do. +} + +// ---------------------------------------------------------------------- + +StyleImage *StyleImage::ExternalWidgetImgRenderer::getBackgroundImage () +{ + Style *style = getStyle (); + return style ? style->backgroundImage : NULL; +} + +BackgroundRepeat StyleImage::ExternalWidgetImgRenderer::getBackgroundRepeat () +{ + Style *style = getStyle (); + return style ? style->backgroundRepeat : BACKGROUND_REPEAT; +} + +BackgroundAttachment + StyleImage::ExternalWidgetImgRenderer::getBackgroundAttachment () +{ + Style *style = getStyle (); + return style ? style->backgroundAttachment : BACKGROUND_ATTACHMENT_SCROLL; +} + +Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionX () +{ + Style *style = getStyle (); + return style ? style->backgroundPositionX : createPerLength (0); +} + +Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionY () +{ + Style *style = getStyle (); + return style ? style->backgroundPositionY : createPerLength (0); +} + +// ---------------------------------------------------------------------- + /* * The drawBorder{Top,Bottom,Left,Right} functions are similar. They * use a trapezium as draw polygon, or drawTypedLine() for dots and dashes. @@ -823,8 +1072,11 @@ static void drawBorderRight(View *view, Style *style, * \brief Draw the border of a region in window, according to style. * * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + * + * "area" is the area to be drawn, "x", "y", "width" and "height" + * define the box itself. All are given in canvas coordinates. */ -void drawBorder (View *view, Rectangle *area, +void drawBorder (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, Style *style, bool inverse) { @@ -861,29 +1113,193 @@ void drawBorder (View *view, Rectangle *area, * according to style. * * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox. + * + * "area" is the area to be drawn, "x", "y", "width" and "height" + * define the box itself (padding box). "xRef", "yRef", "widthRef" and + * "heightRef" define the reference area, which is important for the + * tiling of background images (for position 0%/0%, a tile is set at + * xRef/yRef; for position 100%/100%, a tile is set at xRef + + * widthRef/yRef + widthRef). See calls for more informations; in most + * cases, these boxes are identical (padding box). All these + * coordinates are given in canvas coordinates. + * + * "atTop" should be true, only if the area is drawn directly on the + * canvas, not on top of other areas; this is only true for the + * toplevel widget itself (not parts of its contents). Toplevel widget + * background colors are already set as viewport background color, so + * that drawing again is is not neccessary, but some time can be + * saved. + * + * Otherwise, the caller should not try to increase the performance by + * doing some tests before; this is all done in this method. */ -void drawBackground (View *view, Rectangle *area, +void drawBackground (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, - Style *style, bool inverse) -{ - Rectangle bgArea, intersection; - - if (style->backgroundColor) { - bgArea.x = x + style->margin.left + style->borderWidth.left; - bgArea.y = y + style->margin.top + style->borderWidth.top; - bgArea.width = - width - style->margin.left - style->borderWidth.left - - style->margin.right - style->borderWidth.right; - bgArea.height = - height - style->margin.top - style->borderWidth.top - - style->margin.bottom - style->borderWidth.bottom; - - if (area->intersectsWith (&bgArea, &intersection)) - view->drawRectangle (style->backgroundColor, - inverse ? - Color::SHADING_INVERSE : Color::SHADING_NORMAL, - true, intersection.x, intersection.y, - intersection.width, intersection.height); + int xRef, int yRef, int widthRef, int heightRef, + Style *style, bool inverse, bool atTop) +{ + bool bgColor = style->backgroundColor != NULL && + // The test for background colors is rather simple, since only the color + // has to be compared, ... + (!atTop || layout->getBgColor () != style->backgroundColor); + bool bgImage = (style->backgroundImage != NULL && + style->backgroundImage->getImgbufSrc() != NULL) && + // ... but for backgrounds, it would be rather complicated. To handle the + // two cases (normal HTML in a viewport, where the layout background + // image is set, and contents of <button> within a flat view, where the + // background image of the toplevel widget is set), only the background + // images are compared. A full test, which also deals with all other + // attributes related to backgrond images (repeat, position etc.) would + // be complicated and useless, so not worth the work. + (!atTop || layout->getBgImage () != style->backgroundImage); + + // Since widgets are always drawn from top to bottom, it is *not* + // necessary to draw the background if background color and image + // are not set (NULL), i. e. shining through. + + if (bgColor || bgImage) { + Rectangle bgArea, intersection; + bgArea.x = x; + bgArea.y = y; + bgArea.width = width; + bgArea.height = height; + + if (area->intersectsWith (&bgArea, &intersection)) { + if (bgColor) + view->drawRectangle (style->backgroundColor, + inverse ? + Color::SHADING_INVERSE : Color::SHADING_NORMAL, + true, intersection.x, intersection.y, + intersection.width, intersection.height); + + if (bgImage) + drawBackgroundImage (view, style->backgroundImage, + style->backgroundRepeat, + style->backgroundAttachment, + style->backgroundPositionX, + style->backgroundPositionY, + intersection.x, intersection.y, + intersection.width, intersection.height, + xRef, yRef, widthRef, heightRef); + + } + } +} + +void drawBackgroundImage (View *view, StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef) +{ + //printf ("drawBackgroundImage (..., [img: %d, %d], ..., (%d, %d), %d x %d, " + // "(%d, %d), %d x %d)\n", imgWidth, imgHeight, x, y, width, height, + // xRef, yRef, widthRef, heightRef); + + bool repeatX, repeatY, doDraw; + int origX, origY, tileX1, tileX2, tileY1, tileY2; + + calcBackgroundRelatedValues (backgroundImage, backgroundRepeat, + backgroundAttachment, backgroundPositionX, + backgroundPositionY, x, y, width, height, + xRef, yRef, widthRef, heightRef, + &repeatX, &repeatY, &origX, &origY, + &tileX1, &tileX2, &tileY1, &tileY2, &doDraw); + + //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n", + // tileX1, tileX2, tileY1, tileY2); + + if (doDraw) { + // Drawing is done with the "tiled" buffer, but all calculations + // before have been done with the "source" buffer. + + Imgbuf *imgbufS = backgroundImage->getImgbufSrc(); + int imgWidthS = imgbufS->getRootWidth (); + int imgHeightS = imgbufS->getRootHeight (); + + Imgbuf *imgbufT = backgroundImage->getImgbufTiled(); + int imgWidthT = imgbufT->getRootWidth (); + int imgHeightT = imgbufT->getRootHeight (); + int tilesX = backgroundImage->getTilesX (); + int tilesY = backgroundImage->getTilesY (); + + for (int tileX = tileX1; tileX <= tileX2; tileX += tilesX) + for (int tileY = tileY1; tileY <= tileY2; tileY += tilesY) { + int xt = origX + tileX * imgWidthS; + int x1 = misc::max (xt, x); + int x2 = misc::min (xt + imgWidthT, x + width); + int yt = origY + tileY * imgHeightS; + int y1 = misc::max (yt, y); + int y2 = misc::min (yt + imgHeightT, y + height); + + view->drawImage (imgbufT, xt, yt, x1 - xt, y1 - yt, + x2 - x1, y2 - y1); + } + } +} + +void calcBackgroundRelatedValues (StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int xDraw, int yDraw, int widthDraw, + int heightDraw, int xRef, int yRef, + int widthRef, int heightRef, bool *repeatX, + bool *repeatY, int *origX, int *origY, + int *tileX1, int *tileX2, int *tileY1, + int *tileY2, bool *doDraw) +{ + Imgbuf *imgbuf = backgroundImage->getImgbufSrc(); + int imgWidth = imgbuf->getRootWidth (); + int imgHeight = imgbuf->getRootHeight (); + + *repeatX = backgroundRepeat == BACKGROUND_REPEAT || + backgroundRepeat == BACKGROUND_REPEAT_X; + *repeatY = backgroundRepeat == BACKGROUND_REPEAT || + backgroundRepeat == BACKGROUND_REPEAT_Y; + + *origX = xRef + + (isPerLength (backgroundPositionX) ? + multiplyWithPerLength (widthRef - imgWidth, backgroundPositionX) : + absLengthVal (backgroundPositionX)); + *origY = yRef + + (isPerLength (backgroundPositionY) ? + multiplyWithPerLength (heightRef - imgHeight, backgroundPositionY) : + absLengthVal (backgroundPositionY)); + + *tileX1 = xDraw < *origX ? + - (*origX - xDraw + imgWidth - 1) / imgWidth : + (xDraw - *origX) / imgWidth; + *tileX2 = *origX < xDraw + widthDraw ? + (xDraw + widthDraw - *origX - 1) / imgWidth : + - (*origX - (xDraw + widthDraw) + imgWidth - 1) / imgWidth; + *tileY1 = yDraw < *origY ? + - (*origY - yDraw + imgHeight - 1) / imgHeight : + (yDraw - *origY) / imgHeight; + *tileY2 = *origY < yDraw + heightDraw ? + (yDraw + heightDraw - *origY - 1) / imgHeight : + - (*origY - (yDraw + heightDraw) + imgHeight - 1) / imgHeight; + + *doDraw = true; + if (!*repeatX) { + // Only center tile (tileX = 0) is drawn, ... + if (*tileX1 <= 0 && *tileX2 >= 0) + // ... and is visible. + *tileX1 = *tileX2 = 0; + else + // ... but is not visible. + *doDraw = false; + } + + if (!*repeatY) { + // Analogue. + if (*tileY1 <= 0 && *tileY2 >= 0) + *tileY1 = *tileY2 = 0; + else + *doDraw = false; } } diff --git a/dw/style.hh b/dw/style.hh index c86446a3..ce8e2e6f 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -7,6 +7,8 @@ # error Do not include this file directly, use "core.hh" instead. #endif +#include "../lout/signal.hh" + namespace dw { namespace core { @@ -228,6 +230,18 @@ enum BorderStyle { BORDER_OUTSET }; +enum BackgroundRepeat { + BACKGROUND_REPEAT, + BACKGROUND_REPEAT_X, + BACKGROUND_REPEAT_Y, + BACKGROUND_NO_REPEAT +}; + +enum BackgroundAttachment { + BACKGROUND_ATTACHMENT_SCROLL, + BACKGROUND_ATTACHMENT_FIXED +}; + enum TextAlignType { TEXT_ALIGN_LEFT, TEXT_ALIGN_RIGHT, @@ -463,9 +477,10 @@ public: } }; +class Tooltip; class Font; class Color; -class Tooltip; +class StyleImage; /** * \sa dw::core::style @@ -477,6 +492,11 @@ public: int textDecoration; /* No TextDecoration because of problems converting * TextDecoration <-> int */ Color *color, *backgroundColor; + StyleImage *backgroundImage; + BackgroundRepeat backgroundRepeat; + BackgroundAttachment backgroundAttachment; + Length backgroundPositionX; // "left" defined by "0%" etc. (see CSS spec) + Length backgroundPositionY; // "top" defined by "0%" etc. (see CSS spec) TextAlignType textAlign; VAlignType valign; @@ -536,7 +556,8 @@ public: } inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } - inline bool hasBackground () { return backgroundColor != NULL; } + inline bool hasBackground () + { return backgroundColor != NULL || backgroundImage != NULL; } bool equals (lout::object::Object *other); int hashValue (); @@ -709,12 +730,137 @@ public: { if (--refCount == 0) delete this; } }; -void drawBorder (View *view, Rectangle *area, + +class StyleImage: public lout::signal::ObservedObject +{ +private: + class StyleImgRenderer: public ImgRenderer + { + private: + StyleImage *image; + + public: + inline StyleImgRenderer (StyleImage *image) { this->image = image; } + + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + }; + + int refCount, tilesX, tilesY; + Imgbuf *imgbufSrc, *imgbufTiled; + ImgRendererDist *imgRendererDist; + StyleImgRenderer *styleImgRenderer; + + StyleImage (); + ~StyleImage (); + +public: + /** + * \brief Useful (but not mandatory) base class for updates of + * areas with background images. + */ + class ExternalImgRenderer: public ImgRenderer + { + public: + void setBuffer (core::Imgbuf *buffer, bool resize); + void drawRow (int row); + void finish (); + void fatal (); + + /** + * \brief If this method returns false, nothing is done at all. + */ + virtual bool readyToDraw () = 0; + + /** + * \brief Return the area covered by the background image. + */ + virtual void getBgArea (int *x, int *y, int *width, int *height) = 0; + + /** + * \brief Return the "reference area". + * + * See comment of "drawBackground". + */ + virtual void getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) = 0; + + virtual StyleImage *getBackgroundImage () = 0; + virtual BackgroundRepeat getBackgroundRepeat () = 0; + virtual BackgroundAttachment getBackgroundAttachment () = 0; + virtual Length getBackgroundPositionX () = 0; + virtual Length getBackgroundPositionY () = 0; + + /** + * \brief Draw (or queue for drawing) an area, which is given in + * canvas coordinates. + */ + virtual void draw (int x, int y, int width, int height) = 0; + }; + + /** + * \brief Suitable for widgets and parts of widgets. + */ + class ExternalWidgetImgRenderer: public ExternalImgRenderer + { + public: + void getPaddingArea (int *x, int *y, int *width, int *height); + + StyleImage *getBackgroundImage (); + BackgroundRepeat getBackgroundRepeat (); + BackgroundAttachment getBackgroundAttachment (); + Length getBackgroundPositionX (); + Length getBackgroundPositionY (); + + /** + * \brief Return the style this background image is part of. + */ + virtual Style *getStyle () = 0; + }; + + static StyleImage *create () { return new StyleImage (); } + + inline void ref () { refCount++; } + inline void unref () + { if (--refCount == 0) delete this; } + + inline Imgbuf *getImgbufSrc () { return imgbufSrc; } + inline Imgbuf *getImgbufTiled () + { return imgbufTiled ? imgbufTiled : imgbufSrc; } + inline int getTilesX () { return imgbufTiled ? tilesX : 1; } + inline int getTilesY () { return imgbufTiled ? tilesY : 1; } + inline ImgRenderer *getMainImgRenderer () { return imgRendererDist; } + + /** + * \brief Add an additional ImgRenderer, especially used for + * drawing. + */ + inline void putExternalImgRenderer (ImgRenderer *ir) + { imgRendererDist->put (ir); } + + /** + * \brief Remove a previously added additional ImgRenderer. + */ + inline void removeExternalImgRenderer (ImgRenderer *ir) + { imgRendererDist->remove (ir); } +}; + +void drawBorder (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, Style *style, bool inverse); -void drawBackground (View *view, Rectangle *area, +void drawBackground (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, - Style *style, bool inverse); + int xRef, int yRef, int widthRef, int heightRef, + Style *style, bool inverse, bool atTop); +void drawBackgroundImage (View *view, StyleImage *backgroundImage, + BackgroundRepeat backgroundRepeat, + BackgroundAttachment backgroundAttachment, + Length backgroundPositionX, + Length backgroundPositionY, + int x, int y, int width, int height, + int xRef, int yRef, int widthRef, int heightRef); void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType); } // namespace style diff --git a/dw/textblock.cc b/dw/textblock.cc index 3bedcdba..a45b3da5 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -41,6 +41,103 @@ namespace dw { int Textblock::CLASS_ID = -1; +Textblock::WordImgRenderer::WordImgRenderer (Textblock *textblock, + int wordNo) +{ + //printf ("new WordImgRenderer %p\n", this); + + this->textblock = textblock; + this->wordNo = wordNo; + dataSet = false; +} + +Textblock::WordImgRenderer::~WordImgRenderer () +{ + //printf ("delete WordImgRenderer %p\n", this); +} + +void Textblock::WordImgRenderer::setData (int xWordWidget, int lineNo) +{ + dataSet = true; + this->xWordWidget = xWordWidget; + this->lineNo = lineNo; +} + +bool Textblock::WordImgRenderer::readyToDraw () +{ + //print (); + //printf ("\n"); + + return dataSet && textblock->wasAllocated () + && wordNo < textblock->words->size() + && lineNo < textblock->lines->size(); +} + +void Textblock::WordImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + // TODO Subtract margin and border (padding box)? + Line *line = textblock->lines->getRef (lineNo); + *x = textblock->allocation.x + this->xWordWidget; + *y = textblock->lineYOffsetCanvas (line); + *width = textblock->words->getRef(wordNo)->size.width; + *height = line->boxAscent + line->boxDescent; +} + +void Textblock::WordImgRenderer::getRefArea (int *xRef, int *yRef, + int *widthRef, int *heightRef) +{ + // See comment in Widget::drawBox about the reference area.q + textblock->getPaddingArea (xRef, yRef, widthRef, heightRef); +} + +core::style::Style *Textblock::WordImgRenderer::getStyle () +{ + return textblock->words->getRef(wordNo)->style; +} + +void Textblock::WordImgRenderer::draw (int x, int y, int width, int height) +{ + textblock->queueDrawArea (x - textblock->allocation.x, + y - textblock->allocation.y, width, height); + +} + +void Textblock::WordImgRenderer::print () +{ + printf ("%p: word #%d, ", this, wordNo); + if (wordNo < textblock->words->size()) + textblock->printWordShort (textblock->words->getRef(wordNo)); + else + printf ("<word %d does not exist>", wordNo); + printf (", data set: %s", dataSet ? "yes" : "no"); +} + +void Textblock::SpaceImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + WordImgRenderer::getBgArea (x, y, width, height); + *x += *width; + *width = textblock->words->getRef(wordNo)->effSpace; +} + +core::style::Style *Textblock::SpaceImgRenderer::getStyle () +{ + return textblock->words->getRef(wordNo)->spaceStyle; +} + +void Textblock::SpaceImgRenderer::print () +{ + printf ("%p: word FOR SPACE #%d, ", this, wordNo); + if (wordNo < textblock->words->size()) + textblock->printWordShort (textblock->words->getRef(wordNo)); + else + printf ("<word %d does not exist>", wordNo); + printf (", data set: %s", dataSet ? "yes" : "no"); +} + +// ---------------------------------------------------------------------- + Textblock::DivChar Textblock::divChars[NUM_DIV_CHARS] = { // soft hyphen (U+00AD) { "\xc2\xad", true, false, true, PENALTY_HYPHEN, -1 }, @@ -188,8 +285,13 @@ Textblock::~Textblock () for (int i = 0; i < words->size(); i++) { Word *word = words->getRef (i); + if (word->content.type == core::Content::WIDGET) delete word->content.widget; + + removeWordImgRenderer (i); + removeSpaceImgRenderer (i); + word->style->unref (); word->spaceStyle->unref (); } @@ -1354,14 +1456,77 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, short flags, core::style::Style *style) { words->increase (); - Word *word = words->getLastRef (); - fillWord (word, width, ascent, descent, flags, style); - return word; + int wordNo = words->size () - 1; + initWord (wordNo); + fillWord (wordNo, width, ascent, descent, flags, style); + return words->getRef (wordNo);; +} + +/** + * Basic initialization, which is neccessary before fillWord. + */ +void Textblock::initWord (int wordNo) +{ + Word *word = words->getRef (wordNo); + + word->style = word->spaceStyle = NULL; + word->wordImgRenderer = NULL; + word->spaceImgRenderer = NULL; +} + +void Textblock::removeWordImgRenderer (int wordNo) +{ + Word *word = words->getRef (wordNo); + + if (word->style && word->wordImgRenderer) { + word->style->backgroundImage->removeExternalImgRenderer + (word->wordImgRenderer); + delete word->wordImgRenderer; + word->wordImgRenderer = NULL; + } } -void Textblock::fillWord (Word *word, int width, int ascent, int descent, +void Textblock::setWordImgRenderer (int wordNo) +{ + Word *word = words->getRef (wordNo); + + if (word->style->backgroundImage) { + word->wordImgRenderer = new WordImgRenderer (this, wordNo); + word->style->backgroundImage->putExternalImgRenderer + (word->wordImgRenderer); + } else + word->wordImgRenderer = NULL; +} + +void Textblock::removeSpaceImgRenderer (int wordNo) +{ + Word *word = words->getRef (wordNo); + + if (word->spaceStyle && word->spaceImgRenderer) { + word->spaceStyle->backgroundImage->removeExternalImgRenderer + (word->spaceImgRenderer); + delete word->spaceImgRenderer; + word->spaceImgRenderer = NULL; + } +} + +void Textblock::setSpaceImgRenderer (int wordNo) +{ + Word *word = words->getRef (wordNo); + + if (word->spaceStyle->backgroundImage) { + word->spaceImgRenderer = new SpaceImgRenderer (this, wordNo); + word->spaceStyle->backgroundImage->putExternalImgRenderer + (word->spaceImgRenderer); + } else + word->spaceImgRenderer = NULL; +} + +void Textblock::fillWord (int wordNo, int width, int ascent, int descent, short flags, core::style::Style *style) { + Word *word = words->getRef (wordNo); + word->size.width = width; word->size.ascent = ascent; word->size.descent = descent; @@ -1371,8 +1536,15 @@ void Textblock::fillWord (Word *word, int width, int ascent, int descent, word->content.space = false; word->flags = flags; + removeWordImgRenderer (wordNo); + removeSpaceImgRenderer (wordNo); + word->style = style; word->spaceStyle = style; + + setWordImgRenderer (wordNo); + setSpaceImgRenderer (wordNo); + style->ref (); style->ref (); } @@ -1852,7 +2024,7 @@ void Textblock::addSpace (core::style::Style *style) { int wordIndex = words->size () - 1; if (wordIndex >= 0) { - fillSpace (words->getRef(wordIndex), style); + fillSpace (wordIndex, style); accumulateWordData (wordIndex); correctLastWordExtremes (); } @@ -1874,7 +2046,7 @@ void Textblock::addBreakOption (core::style::Style *style, bool forceBreak) } } -void Textblock::fillSpace (Word *word, core::style::Style *style) +void Textblock::fillSpace (int wordNo, core::style::Style *style) { // Old comment: // @@ -1890,6 +2062,8 @@ void Textblock::fillSpace (Word *word, core::style::Style *style) // // TODO: Re-evaluate again. + Word *word = words->getRef (wordNo); + // TODO: This line does not work: addBreakOption (word, style); // Do not override a previously set break penalty. @@ -1907,9 +2081,14 @@ void Textblock::fillSpace (Word *word, core::style::Style *style) //DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex, // word->content.space); + + removeSpaceImgRenderer (wordNo); + word->spaceStyle->unref (); word->spaceStyle = style; style->ref (); + + setSpaceImgRenderer (wordNo); } } diff --git a/dw/textblock.hh b/dw/textblock.hh index 7cf62f5c..b85937ba 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -237,6 +237,44 @@ private: static const char *hyphenDrawChar; protected: + /** + * \brief Implementation used for words. + */ + class WordImgRenderer: + public core::style::StyleImage::ExternalWidgetImgRenderer + { + protected: + Textblock *textblock; + int wordNo, xWordWidget, lineNo; + bool dataSet; + + public: + WordImgRenderer (Textblock *textblock, int wordNo); + ~WordImgRenderer (); + + void setData (int xWordWidget, int lineNo); + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + core::style::Style *getStyle (); + void draw (int x, int y, int width, int height); + + virtual void print (); + }; + + class SpaceImgRenderer: public WordImgRenderer + { + public: + inline SpaceImgRenderer (Textblock *textblock, int wordNo) : + WordImgRenderer (textblock, wordNo) { } + + void getBgArea (int *x, int *y, int *width, int *height); + core::style::Style *getStyle (); + + void print (); + }; + struct Paragraph { int firstWord; /* first word's index in word vector */ @@ -350,6 +388,11 @@ protected: core::style::Style *style; core::style::Style *spaceStyle; /* initially the same as of the word, later set by a_Dw_page_add_space */ + + // These two are used rarely, so there is perhaps a way to store + // them which is consuming less memory. + WordImgRenderer *wordImgRenderer; + SpaceImgRenderer *spaceImgRenderer; }; void printWordShort (Word *word); @@ -483,9 +526,14 @@ protected: Word *addWord (int width, int ascent, int descent, short flags, core::style::Style *style); - void fillWord (Word *word, int width, int ascent, int descent, + void initWord (int wordNo); + void removeWordImgRenderer (int wordNo); + void setWordImgRenderer (int wordNo); + void removeSpaceImgRenderer (int wordNo); + void setSpaceImgRenderer (int wordNo); + void fillWord (int wordNo, int width, int ascent, int descent, short flags, core::style::Style *style); - void fillSpace (Word *word, core::style::Style *style); + void fillSpace (int wordNo, core::style::Style *style); void setBreakOption (Word *word, core::style::Style *style, int breakPenalty1, int breakPenalty2, bool forceBreak); bool isBreakAllowed (Word *word); diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index 226dc3c7..c07dc602 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -420,6 +420,16 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, //printf (": "); //words->getRef(line->lastWord)->badnessAndPenalty.print (); //printf ("\n"); + + int xWidget = lineXOffsetWidget(line); + for (int i = firstWord; i <= lastWord; i++) { + Word *word = words->getRef (i); + if (word->wordImgRenderer) + word->wordImgRenderer->setData (xWidget, lines->size () - 1); + if (word->spaceImgRenderer) + word->spaceImgRenderer->setData (xWidget, lines->size () - 1); + xWidget += word->size.width + word->effSpace; + } return line; } @@ -872,6 +882,8 @@ int Textblock::hyphenateWord (int wordIndex) PRINTF ("[%p] %d words ...\n", this, words->size ()); words->insert (wordIndex, numBreaks); + for (int i = 0; i < numBreaks; i++) + initWord (wordIndex + i); PRINTF ("[%p] ... => %d words\n", this, words->size ()); // Adjust anchor indexes. @@ -883,8 +895,7 @@ int Textblock::hyphenateWord (int wordIndex) for (int i = 0; i < numBreaks + 1; i++) { Word *w = words->getRef (wordIndex + i); - - fillWord (w, wordSize[i].width, wordSize[i].ascent, + fillWord (wordIndex + i, wordSize[i].width, wordSize[i].ascent, wordSize[i].descent, false, origWord.style); // TODO There should be a method fillText0. @@ -923,7 +934,7 @@ int Textblock::hyphenateWord (int wordIndex) PRINTF (" [%d] + hyphen\n", wordIndex + i); } else { if (origWord.content.space) { - fillSpace (w, origWord.spaceStyle); + fillSpace (wordIndex + i, origWord.spaceStyle); PRINTF (" [%d] + space\n", wordIndex + i); } else { PRINTF (" [%d] + nothing\n", wordIndex + i); diff --git a/dw/widget.cc b/dw/widget.cc index 9c6fe380..415da6bc 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -32,6 +32,36 @@ namespace core { // ---------------------------------------------------------------------- +bool Widget::WidgetImgRenderer::readyToDraw () +{ + return widget->wasAllocated (); +} + +void Widget::WidgetImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + widget->getPaddingArea (x, y, width, height); +} + +void Widget::WidgetImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) +{ + widget->getPaddingArea (xRef, yRef, widthRef, heightRef); +} + +style::Style *Widget::WidgetImgRenderer::getStyle () +{ + return widget->getStyle (); +} + +void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height) +{ + widget->queueDrawArea (x - widget->allocation.x, y - widget->allocation.y, + width, height); +} + +// ---------------------------------------------------------------------- + int Widget::CLASS_ID = -1; Widget::Widget () @@ -55,6 +85,8 @@ Widget::Widget () deleteCallbackData = NULL; deleteCallbackFunc = NULL; + + widgetImgRenderer = NULL; } Widget::~Widget () @@ -62,6 +94,12 @@ Widget::~Widget () if (deleteCallbackFunc) deleteCallbackFunc (deleteCallbackData); + if (widgetImgRenderer) { + if (style && style->backgroundImage) + style->backgroundImage->removeExternalImgRenderer (widgetImgRenderer); + delete widgetImgRenderer; + } + if (style) style->unref (); @@ -281,6 +319,10 @@ void Widget::setStyle (style::Style *style) { bool sizeChanged; + if (widgetImgRenderer && this->style && this->style->backgroundImage) + this->style->backgroundImage->removeExternalImgRenderer + (widgetImgRenderer); + style->ref (); if (this->style) { @@ -291,6 +333,15 @@ void Widget::setStyle (style::Style *style) this->style = style; + if (style && style->backgroundImage) { + // Create instance of WidgetImgRenderer when needed. Until this + // widget is deleted, "widgetImgRenderer" will be kept, since it + // is not specific to the style, but only to this widget. + if (widgetImgRenderer == NULL) + widgetImgRenderer = new WidgetImgRenderer (this); + style->backgroundImage->putExternalImgRenderer (widgetImgRenderer); + } + if (layout != NULL) { layout->updateCursor (); } @@ -340,20 +391,31 @@ style::Color *Widget::getBgColor () void Widget::drawBox (View *view, style::Style *style, Rectangle *area, int x, int y, int width, int height, bool inverse) { - Rectangle viewArea; - viewArea.x = area->x + allocation.x; - viewArea.y = area->y + allocation.y; - viewArea.width = area->width; - viewArea.height = area->height; + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; - style::drawBorder (view, &viewArea, allocation.x + x, allocation.y + y, + style::drawBorder (view, layout, &canvasArea, + allocation.x + x, allocation.y + y, width, height, style, inverse); - /** \todo Background images? */ - if (style->backgroundColor) - style::drawBackground (view, &viewArea, - allocation.x + x, allocation.y + y, width, height, - style, inverse); + // This method is used for inline elements, where the CSS 2 specification + // does not define what here is called "reference area". To make it look + // smoothly, the widget padding box is used. + + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + style::drawBackground + (view, layout, &canvasArea, + allocation.x + x + style->margin.left + style->borderWidth.left, + allocation.y + y + style->margin.top + style->borderWidth.top, + width - style->margin.left - style->borderWidth.left + - style->margin.right - style->borderWidth.right, + height - style->margin.top - style->borderWidth.top + - style->margin.bottom - style->borderWidth.bottom, + xPad, yPad, widthPad, heightPad, style, inverse, false); } /** @@ -364,32 +426,21 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, */ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) { - Rectangle viewArea; - viewArea.x = area->x + allocation.x; - viewArea.y = area->y + allocation.y; - viewArea.width = area->width; - viewArea.height = area->height; + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; - style::drawBorder (view, &viewArea, allocation.x, allocation.y, + style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y, allocation.width, getHeight (), style, inverse); - /** \todo Adjust following comment from the old dw sources. */ - /* - * - Toplevel widget background colors are set as viewport - * background color. This is not crucial for the rendering, but - * looks a bit nicer when scrolling. Furthermore, the viewport - * does anything else in this case. - * - * - Since widgets are always drawn from top to bottom, it is - * *not* necessary to draw the background if - * widget->style->background_color is NULL (shining through). - */ - /** \todo Background images? */ - - if (style->backgroundColor && - (parent || layout->getBgColor () != style->backgroundColor)) - style::drawBackground (view, &viewArea, allocation.x, allocation.y, - allocation.width, getHeight (), style, inverse); + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + style::drawBackground (view, layout, &canvasArea, + xPad, yPad, widthPad, heightPad, + xPad, yPad, widthPad, heightPad, + style, inverse, parent == NULL); } /* @@ -533,6 +584,23 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos, x + allocation.x, y + allocation.y, width, height); } +/** + * \brief Return the padding area (content plus padding). + * + * Used as "reference area" (ee comment of "style::drawBackground") + * for backgrounds. + */ +void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, + int *heightPad) +{ + *xPad = allocation.x + style->margin.left + style->borderWidth.left; + *yPad = allocation.y + style->margin.top + style->borderWidth.top; + *widthPad = allocation.width - style->margin.left - style->borderWidth.left + - style->margin.right - style->borderWidth.right; + *heightPad = getHeight () - style->margin.top - style->borderWidth.top + - style->margin.bottom - style->borderWidth.bottom; +} + void Widget::getExtremesImpl (Extremes *extremes) { /* Simply return the requisition width */ diff --git a/dw/widget.hh b/dw/widget.hh index 6942221b..34b35efa 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -74,6 +74,28 @@ protected: BLOCK_LEVEL = 1 << 6, }; + /** + * \brief Implementation which represents the whole widget. + * + * The only instance is set created needed. + */ + class WidgetImgRenderer: public style::StyleImage::ExternalWidgetImgRenderer + { + private: + Widget *widget; + + public: + inline WidgetImgRenderer (Widget *widget) { this->widget = widget; } + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + style::Style *getStyle (); + void draw (int x, int y, int width, int height); + }; + + WidgetImgRenderer *widgetImgRenderer; + private: /** * \brief The parent widget, NULL for top-level widgets. @@ -289,6 +311,8 @@ public: void scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height); + void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad); + /** * \brief Return an iterator for this widget. * @@ -45,6 +45,8 @@ typedef enum { opposed to CSS_TYPE_ENUM and CSS_TYPE_MULTI_ENUM). Used for 'font-family'. */ + CSS_TYPE_URI, /* <uri> */ + CSS_TYPE_BACKGROUND_POSITION, CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some day. */ } CssValueType; @@ -229,9 +231,15 @@ typedef enum { CSS_PROPERTY_LAST } CssPropertyName; +typedef struct { + int32_t posX; + int32_t posY; +} CssBackgroundPosition; + typedef union { int32_t intVal; char *strVal; + CssBackgroundPosition *posVal; } CssPropertyValue; typedef enum { @@ -285,6 +293,8 @@ class CssProperty { case CSS_TYPE_SYMBOL: dFree (value.strVal); break; + case CSS_TYPE_BACKGROUND_POSITION: + dFree (value.posVal); default: break; } diff --git a/src/cssparser.cc b/src/cssparser.cc index eda45472..0d0ce5a1 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -47,6 +47,14 @@ typedef struct { const char *const *enum_symbols; } CssPropertyInfo; +static const char *const Css_background_attachment_enum_vals[] = { + "scroll", "fixed", NULL +}; + +static const char *const Css_background_repeat_enum_vals[] = { + "repeat", "repeat-x", "repeat-y", "no-repeat", NULL +}; + static const char *const Css_border_collapse_enum_vals[] = { "separate", "collapse", NULL }; @@ -139,11 +147,14 @@ static const char *const Css_word_spacing_enum_vals[] = { }; const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { - {"background-attachment", {CSS_TYPE_UNUSED}, NULL}, + {"background-attachment", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, + Css_background_attachment_enum_vals}, {"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL}, - {"background-image", {CSS_TYPE_UNUSED}, NULL}, - {"background-position", {CSS_TYPE_UNUSED}, NULL}, - {"background-repeat", {CSS_TYPE_UNUSED}, NULL}, + {"background-image", {CSS_TYPE_URI, CSS_TYPE_UNUSED}, NULL}, + {"background-position", {CSS_TYPE_BACKGROUND_POSITION, CSS_TYPE_UNUSED}, + NULL}, + {"background-repeat", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, + Css_background_repeat_enum_vals}, {"border-bottom-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, Css_border_color_enum_vals}, {"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, @@ -694,6 +705,15 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) } break; + case CSS_TYPE_BACKGROUND_POSITION: + if (ttype == CSS_TK_SYMBOL && + (dStrAsciiCasecmp(tval, "center") == 0 || + dStrAsciiCasecmp(tval, "left") == 0 || + dStrAsciiCasecmp(tval, "right") == 0 || + dStrAsciiCasecmp(tval, "top") == 0 || + dStrAsciiCasecmp(tval, "bottom") == 0)) + return true; + // Fall Through (lenght and percentage) case CSS_TYPE_LENGTH_PERCENTAGE: case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER: case CSS_TYPE_LENGTH: @@ -734,6 +754,12 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) } break; + case CSS_TYPE_URI: + if (ttype == CSS_TK_SYMBOL && + dStrAsciiCasecmp(tval, "url") == 0) + return true; + break; + case CSS_TYPE_UNUSED: case CSS_TYPE_INTEGER: /* Not used for parser values. */ @@ -1004,6 +1030,107 @@ bool CssParser::parseValue(CssPropertyName prop, } break; + case CSS_TYPE_URI: + if (ttype == CSS_TK_SYMBOL && + dStrAsciiCasecmp(tval, "url") == 0) { + val->strVal = parseUrl(); + nextToken(); + if (val->strVal) + ret = true; + } + break; + + case CSS_TYPE_BACKGROUND_POSITION: + // 'background-position' consists of one or two values: vertical and + // horizontal position; in most cases in this order. However, as long it + // is unambigous, the order can be switched: "10px left" and "left 10px" + // are both possible and have the same effect. For this reason, all + // possibilities are tested parrallel. + + bool h[2], v[2]; + int pos[2]; + h[0] = v[0] = h[1] = v[1] = false; + + // First: collect values in pos[0] and pos[1], and determine whether + // they can be used for a horizontal (h[i]) or vertical (v[i]) position + // (or both). When neither h[i] or v[i] is set, pos[i] is undefined. + for (i = 0; i < 2; i++) { + CssValueType typeTmp; + // tokenMatchesProperty will, for CSS_PROPERTY_BACKGROUND_POSITION, + // work on both parts, since they are exchangable. + if (tokenMatchesProperty (CSS_PROPERTY_BACKGROUND_POSITION, + &typeTmp)) { + h[i] = ttype != CSS_TK_SYMBOL || + (dStrAsciiCasecmp(tval, "top") != 0 && + dStrAsciiCasecmp(tval, "bottom") != 0); + v[i] = ttype != CSS_TK_SYMBOL || + (dStrAsciiCasecmp(tval, "left") != 0 && + dStrAsciiCasecmp(tval, "right") != 0); + } else + // No match. + h[i] = v[i] = false; + + if (h[i] || v[i]) { + // Calculate values. + if (ttype == CSS_TK_SYMBOL) { + if (dStrAsciiCasecmp(tval, "top") == 0 || + dStrAsciiCasecmp(tval, "left") == 0) { + pos[i] = CSS_CREATE_LENGTH (0.0, CSS_LENGTH_TYPE_PERCENTAGE); + nextToken(); + } else if (dStrAsciiCasecmp(tval, "center") == 0) { + pos[i] = CSS_CREATE_LENGTH (0.5, CSS_LENGTH_TYPE_PERCENTAGE); + nextToken(); + } else if (dStrAsciiCasecmp(tval, "bottom") == 0 || + dStrAsciiCasecmp(tval, "right") == 0) { + pos[i] = CSS_CREATE_LENGTH (1.0, CSS_LENGTH_TYPE_PERCENTAGE); + nextToken(); + } else + // tokenMatchesProperty should have returned "false" already. + lout::misc::assertNotReached (); + } else { + // We can assume <length> or <percentage> here ... + CssPropertyValue valTmp; + if (parseValue(prop, CSS_TYPE_LENGTH_PERCENTAGE, &valTmp)) { + pos[i] = valTmp.intVal; + ret = true; + } else + // ... but something may still fail. + h[i] = v[i] = false; + } + } + + // If the first value cannot be read, do not read the second. + if (!h[i] && !v[i]) + break; + } + + // Second: Create the final value. Order will be determined here. + if (v[0] || h[0]) { + // If second value is not set, it is set to "center", i. e. 50%, (see + // CSS specification), which is suitable for both dimensions. + if (!h[1] && !v[1]) { + pos[1] = CSS_CREATE_LENGTH (0.5, CSS_LENGTH_TYPE_PERCENTAGE); + h[1] = v[1] = true; + } + + // Only valid, when a combination h/v or v/h is possible. + if ((h[0] && v[1]) || (v[0] && h[1])) { + ret = true; + val->posVal = dNew(CssBackgroundPosition, 1); + + // Prefer combination h/v: + if (h[0] && v[1]) { + val->posVal->posX = pos[0]; + val->posVal->posY = pos[1]; + } else { + // This should be v/h: + val->posVal->posX = pos[1]; + val->posVal->posY = pos[0]; + } + } + } + break; + case CSS_TYPE_UNUSED: /* nothing */ break; diff --git a/src/cssparser.hh b/src/cssparser.hh index 8609877b..30d02eee 100644 --- a/src/cssparser.hh +++ b/src/cssparser.hh @@ -2,7 +2,8 @@ #define __CSSPARSER_HH__ #include "css.hh" -#include "html_common.hh" + +class DilloHtml; class CssParser { private: diff --git a/src/dicache.c b/src/dicache.c index 55232846..ec63df1f 100644 --- a/src/dicache.c +++ b/src/dicache.c @@ -276,7 +276,8 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image, /* BUG: there's just one image-type now */ #define I_RGB 0 - DicEntry->v_imgbuf = a_Imgbuf_new(Image->dw, I_RGB, width, height, gamma); + DicEntry->v_imgbuf = + a_Imgbuf_new(Image->layout, I_RGB, width, height, gamma); DicEntry->TotalSize = width * height * 3; DicEntry->width = width; @@ -402,7 +403,7 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr, dReturn_val_if_fail(MimeType && Ptr, NULL); if (!web->Image) { - web->Image = a_Image_new(NULL, web->bgColor); + web->Image = a_Image_new(NULL, NULL, web->bgColor); a_Image_ref(web->Image); } @@ -429,7 +430,7 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr, *Data = DicEntry->DecoderData; *Call = (CA_Callback_t) a_Dicache_callback; - return (web->Image->dw); + return (web->Image->img_rnd); } /* @@ -516,9 +517,12 @@ void a_Dicache_callback(int Op, CacheClient_t *Client) Image->ScanNumber = DicEntry->ScanNumber; } } - } else if (Op == CA_Close || Op == CA_Abort) { + } else if (Op == CA_Close) { a_Image_close(Image); a_Bw_close_client(Web->bw, Client->Key); + } else if (Op == CA_Abort) { + a_Image_abort(Image); + a_Bw_close_client(Web->bw, Client->Key); } } diff --git a/src/form.cc b/src/form.cc index 8476b73b..b6df56f5 100644 --- a/src/form.cc +++ b/src/form.cc @@ -340,7 +340,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) char *charset, *first; const char *attrbuf; - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); if (html->InFlags & IN_FORM) { BUG_MSG("nested forms\n"); @@ -567,7 +567,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, attrbuf); } - HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle()); + HT2TB(html)->addWidget (embed, html->backgroundStyle()); } dFree(type); dFree(name); @@ -600,17 +600,17 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize) html->charset); html->InFlags |= IN_FORM; - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt"))) - HT2TB(html)->addText(attrbuf, html->styleEngine->wordStyle ()); + HT2TB(html)->addText(attrbuf, html->wordStyle ()); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); EntryResource *entryResource = factory->createEntryResource (20,false,NULL); embed = new Embed (entryResource); Html_add_input(html, DILLO_HTML_INPUT_INDEX, embed, NULL, NULL, FALSE); - HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ()); + HT2TB(html)->addWidget (embed, html->backgroundStyle ()); a_Url_free(action); html->InFlags &= ~IN_FORM; @@ -675,7 +675,7 @@ void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize) textres->setEditable(false); Html_add_input(html, DILLO_HTML_INPUT_TEXTAREA, embed, name, NULL, false); - HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ()); + HT2TB(html)->addWidget (embed, html->backgroundStyle ()); dFree(name); } @@ -766,7 +766,7 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize) html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, attrbuf); } - HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ()); + HT2TB(html)->addWidget (embed, html->backgroundStyle ()); Html_add_input(html, type, embed, name, NULL, false); a_Html_stash_init(html); @@ -938,16 +938,16 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize) * but it caused 100% CPU usage. */ page = new Textblock (false); - page->setStyle (html->styleEngine->backgroundStyle ()); + page->setStyle (html->backgroundStyle ()); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); Resource *resource = factory->createComplexButtonResource(page, true); embed = new Embed(resource); // a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE); - HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); - HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ()); - HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (5, html->wordStyle ()); + HT2TB(html)->addWidget (embed, html->backgroundStyle ()); + HT2TB(html)->addParbreak (5, html->wordStyle ()); S_TOP(html)->textblock = html->dw = page; @@ -2005,12 +2005,16 @@ static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize) /* create new image and add it to the button */ a_Html_image_attrs(html, tag, tagsize); if ((Image = a_Html_image_new(html, tag, tagsize))) { - IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ()); + // At this point, we know that Image->ir represents an image + // widget. Notice that the order of the casts matters, because + // of multiple inheritance. + dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)Image->img_rnd; + dwi->setStyle (html->backgroundStyle ()); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); ComplexButtonResource *complex_b_r = - factory->createComplexButtonResource(IM2DW(Image), false); + factory->createComplexButtonResource(dwi, false); button = new Embed(complex_b_r); - HT2TB(html)->addWidget (button, html->styleEngine->style ()); + HT2TB(html)->addWidget (button, html->style ()); } if (!button) MSG("Html_input_image: unable to create image submit.\n"); diff --git a/src/html.cc b/src/html.cc index 03bcb736..00882ccc 100644 --- a/src/html.cc +++ b/src/html.cc @@ -360,9 +360,9 @@ static void Html_add_textblock(DilloHtml *html, int space) { Textblock *textblock = new Textblock (prefs.limit_text_width); - HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ()); - HT2TB(html)->addWidget (textblock, html->styleEngine->style ()); - HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (space, html->wordStyle ()); + HT2TB(html)->addWidget (textblock, html->style ()); + HT2TB(html)->addParbreak (space, html->wordStyle ()); S_TOP(html)->textblock = html->dw = textblock; S_TOP(html)->hand_over_break = true; } @@ -1040,11 +1040,11 @@ static void Html_process_space_pre_line(DilloHtml *html, const char *space, breakCnt++; html->PrevWasCR = (space[i] == '\r'); - HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ()); + HT2TB(html)->addLinebreak (html->wordStyle ()); } } if (breakCnt == 0) { - HT2TB(html)->addSpace(html->styleEngine->wordStyle ()); + HT2TB(html)->addSpace(html->wordStyle ()); } } @@ -1077,12 +1077,11 @@ static void Html_process_space(DilloHtml *html, const char *space, if (spaceCnt) { spc = dStrnfill(spaceCnt, ' '); - HT2TB(html)->addText (spc, spaceCnt, - html->styleEngine->wordStyle ()); + HT2TB(html)->addText (spc, spaceCnt, html->wordStyle ()); dFree(spc); spaceCnt = 0; } - HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ()); + HT2TB(html)->addLinebreak (html->wordStyle ()); html->pre_column = 0; } html->PreFirstChar = false; @@ -1110,20 +1109,19 @@ static void Html_process_space(DilloHtml *html, const char *space, if (spaceCnt) { // add break possibility for the white-space:pre-wrap case - HT2TB(html)->addBreakOption (html->styleEngine->wordStyle (), false); + HT2TB(html)->addBreakOption (html->wordStyle (), false); spc = dStrnfill(spaceCnt, ' '); - HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ()); + HT2TB(html)->addText (spc, spaceCnt, html->wordStyle ()); dFree(spc); } } else { if (SGML_SPCDEL) { /* SGML_SPCDEL ignores white space immediately after an open tag */ - } else if (html->styleEngine->wordStyle ()->whiteSpace == - WHITE_SPACE_PRE_LINE) { + } else if (html->wordStyle ()->whiteSpace == WHITE_SPACE_PRE_LINE) { Html_process_space_pre_line(html, space, spacesize); } else { - HT2TB(html)->addSpace(html->styleEngine->wordStyle ()); + HT2TB(html)->addSpace(html->wordStyle ()); } if (parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY) @@ -1176,8 +1174,7 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) Html_process_space(html, Pword + start, i - start); } else { while (Pword[++i] && !isspace(Pword[i])) ; - HT2TB(html)->addText(Pword + start, i - start, - html->styleEngine->wordStyle ()); + HT2TB(html)->addText(Pword + start, i - start, html->wordStyle ()); html->pre_column += i - start; html->PreFirstChar = false; } @@ -1215,20 +1212,18 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) Html_process_space(html, word2 + start, i - start); } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) { i += 3; - HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), false); + HT2TB(html)->addBreakOption(html->wordStyle (), false); } else if (a_Utf8_ideographic(word2+i, beyond_word2, &len)) { i += len; - HT2TB(html)->addText(word2 + start, i - start, - html->styleEngine->wordStyle ()); - HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), false); + HT2TB(html)->addText(word2 + start, i - start, html->wordStyle ()); + HT2TB(html)->addBreakOption(html->wordStyle (), false); } else { do { i += len; } while (word2[i] && !isspace(word2[i]) && strncmp(word2+i, utf8_zero_width_space, 3) && (!a_Utf8_ideographic(word2+i, beyond_word2, &len))); - HT2TB(html)->addText(word2 + start, i - start, - html->styleEngine->wordStyle ()); + HT2TB(html)->addText(word2 + start, i - start, html->wordStyle ()); } } if (Pword == word2) @@ -1262,7 +1257,7 @@ static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break) { if (html->dw != S_TOP(html)->textblock) { if (hand_over_break) - HT2TB(html)->handOverBreak (html->styleEngine->style ()); + HT2TB(html)->handOverBreak (html->style ()); HT2TB(html)->flush (); html->dw = S_TOP(html)->textblock; } @@ -1290,7 +1285,7 @@ static void Html_push_tag(DilloHtml *html, int tag_idx) */ static void Html_force_push_tag(DilloHtml *html, int tag_idx) { - html->styleEngine->startElement (tag_idx); + html->startElement (tag_idx); Html_push_tag(html, tag_idx); } @@ -1816,6 +1811,10 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) int32_t color; int tag_index_a = a_Html_tag_index ("a"); style::Color *bgColor; + style::StyleImage *bgImage; + style::BackgroundRepeat bgRepeat; + style::BackgroundAttachment bgAttachment; + style::Length bgPositionX, bgPositionY; _MSG("Html_tag_open_body Num_BODY=%d\n", html->Num_BODY); if (!(html->InFlags & IN_BODY)) @@ -1856,7 +1855,7 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) CSS_TYPE_COLOR, color); } - html->styleEngine->restyle (); + html->restyle (); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link"))) { html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1); @@ -1870,35 +1869,40 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) BUG_MSG("<body> vlink attribute is obsolete.\n"); } - html->dw->setStyle (html->styleEngine->style ()); + html->dw->setStyle (html->style ()); bgColor = html->styleEngine->backgroundColor (); - if (bgColor) HT2LT(html)->setBgColor(bgColor); + bgImage = html->styleEngine->backgroundImage (&bgRepeat, &bgAttachment, + &bgPositionX, &bgPositionY); + if (bgImage) + HT2LT(html)->setBgImage(bgImage, bgRepeat, bgAttachment, bgPositionX, + bgPositionY); + /* Determine a color for visited links. * This color is computed once per page and used for immediate feedback * when clicking a link. * On reload style including color for visited links is computed properly * according to CSS. */ - html->styleEngine->startElement (tag_index_a); + html->startElement (tag_index_a); html->styleEngine->setPseudoVisited (); if (html->non_css_visited_color != -1) { html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR, html->non_css_visited_color); } - html->visited_color = html->styleEngine->style ()->color->getColor (); + html->visited_color = html->style ()->color->getColor (); html->styleEngine->endElement (tag_index_a); if (prefs.contrast_visited_color) { /* get a color that has a "safe distance" from text, link and bg */ html->visited_color = a_Color_vc(html->visited_color, - html->styleEngine->style ()->color->getColor(), + html->style ()->color->getColor(), html->non_css_link_color, - html->styleEngine->backgroundStyle()->backgroundColor->getColor()); + html->backgroundStyle()->backgroundColor->getColor()); } @@ -1970,28 +1974,28 @@ static void src = dStrdup(attrbuf); - textblock->addParbreak (5, html->styleEngine->wordStyle ()); + textblock->addParbreak (5, html->wordStyle ()); bullet = new Bullet(); - textblock->addWidget(bullet, html->styleEngine->wordStyle ()); - textblock->addSpace(html->styleEngine->wordStyle ()); + textblock->addWidget(bullet, html->wordStyle ()); + textblock->addSpace(html->wordStyle ()); if (D_ASCII_TOLOWER(tag[1]) == 'i') { /* IFRAME usually comes with very long advertising/spying URLS, * to not break rendering we will force name="IFRAME" */ - textblock->addText ("IFRAME", html->styleEngine->wordStyle ()); + textblock->addText ("IFRAME", html->wordStyle ()); } else { /* FRAME: * If 'name' tag is present use it, if not use 'src' value */ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) { - textblock->addText (src, html->styleEngine->wordStyle ()); + textblock->addText (src, html->wordStyle ()); } else { - textblock->addText (attrbuf, html->styleEngine->wordStyle ()); + textblock->addText (attrbuf, html->wordStyle ()); } } - textblock->addParbreak (5, html->styleEngine->wordStyle ()); + textblock->addParbreak (5, html->wordStyle ()); dFree(src); } @@ -2004,8 +2008,8 @@ static void static void Html_tag_content_frameset (DilloHtml *html, const char *tag, int tagsize) { - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); - HT2TB(html)->addText("--FRAME--", html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); + HT2TB(html)->addText("--FRAME--", html->wordStyle ()); Html_add_textblock(html, 5); } @@ -2026,7 +2030,7 @@ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_content_br(DilloHtml *html, const char *tag, int tagsize) { - HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ()); + HT2TB(html)->addLinebreak (html->wordStyle ()); } /* @@ -2215,7 +2219,9 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize) alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode } - image = a_Image_new(alt_ptr, 0); + dw::Image *dw = new dw::Image(alt_ptr); + image = + a_Image_new(html->dw->getLayout(), (void*)(dw::core::ImgRenderer*)dw, 0); if (HT2TB(html)->getBgColor()) image->bg_color = HT2TB(html)->getBgColor()->getColor(); @@ -2292,13 +2298,17 @@ static void Html_tag_content_img(DilloHtml *html, const char *tag, int tagsize) /* TODO: usemap URLs outside of the document are not used. */ usemap_url = a_Html_url_new(html, attrbuf, NULL, 0); - HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style()); + // At this point, we know that Image->ir represents an image + // widget. Notice that the order of the casts matters, because of + // multiple inheritance. + dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)Image->img_rnd; + HT2TB(html)->addWidget(dwi, html->style()); /* Image maps */ if (a_Html_get_attr(html, tag, tagsize, "ismap")) { - ((::dw::Image*)Image->dw)->setIsMap(); + dwi->setIsMap(); _MSG(" Html_tag_open_img: server-side map (ISMAP)\n"); - } else if (html->styleEngine->style ()->x_link != -1 && + } else if (html->style ()->x_link != -1 && usemap_url == NULL) { /* For simple links, we have to suppress the "image_pressed" signal. * This is overridden for USEMAP images. */ @@ -2306,8 +2316,7 @@ static void Html_tag_content_img(DilloHtml *html, const char *tag, int tagsize) } if (usemap_url) { - ((::dw::Image*)Image->dw)->setUseMap(&html->maps, - new ::object::String(URL_STR(usemap_url))); + dwi->setUseMap(&html->maps, new ::object::String(URL_STR(usemap_url))); a_Url_free (usemap_url); } } @@ -2350,8 +2359,14 @@ static void Html_tag_close_map(DilloHtml *html) for (int i = 0; i < html->images->size(); i++) { DilloImage *img = html->images->get(i)->image; - if (img) - ((dw::Image*) img->dw)->forceMapRedraw(); + if (img) { + // At this point, we know that img->ir represents an image + // widget. (Really? Is this assumtion safe?) Notice that the + // order of the casts matters, because of multiple + // inheritance. + dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)img->img_rnd; + dwi->forceMapRedraw(); + } } html->InFlags &= ~IN_MAP; } @@ -2494,7 +2509,7 @@ static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize) html->styleEngine->setNonCssHint(PROPERTY_X_LINK, CSS_TYPE_INTEGER, Html_set_new_link(html, &url)); - HT2TB(html)->addText("[OBJECT]", html->styleEngine->wordStyle ()); + HT2TB(html)->addText("[OBJECT]", html->wordStyle ()); } a_Url_free(base_url); } @@ -2528,7 +2543,7 @@ static const char* Html_get_javascript_link(DilloHtml *html) static void Html_add_anchor(DilloHtml *html, const char *name) { _MSG("Registering ANCHOR: %s\n", name); - if (!HT2TB(html)->addAnchor (name, html->styleEngine->style ())) + if (!HT2TB(html)->addAnchor (name, html->style ())) BUG_MSG("Anchor names must be unique within the document ('%s')\n",name); /* * According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that @@ -2637,7 +2652,7 @@ static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize) const char *U201C = "\xe2\x80\x9c"; html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addText (U201C, html->styleEngine->wordStyle ()); + HT2TB(html)->addText (U201C, html->wordStyle ()); } /* @@ -2648,7 +2663,7 @@ static void Html_tag_close_q(DilloHtml *html) /* Right Double Quotation Mark */ const char *U201D = "\xe2\x80\x9d"; - HT2TB(html)->addText (U201D, html->styleEngine->wordStyle ()); + HT2TB(html)->addText (U201D, html->wordStyle ()); } /* @@ -2690,7 +2705,7 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize) { html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); S_TOP(html)->list_type = HTML_LIST_UNORDERED; S_TOP(html)->list_number = 0; @@ -2750,7 +2765,7 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) { - Style *style = html->styleEngine->style (); + Style *style = html->style (); int *list_number; const char *attrbuf; @@ -2845,12 +2860,12 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_content_hr(DilloHtml *html, const char *tag, int tagsize) { Widget *hruler; - HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (5, html->wordStyle ()); hruler = new Ruler(); - hruler->setStyle (html->styleEngine->style ()); - HT2TB(html)->addWidget (hruler, html->styleEngine->style ()); - HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); + hruler->setStyle (html->style ()); + HT2TB(html)->addWidget (hruler, html->style ()); + HT2TB(html)->addParbreak (5, html->wordStyle ()); } /* @@ -2860,7 +2875,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize) { /* may want to actually do some stuff here. */ html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); } /* @@ -2869,7 +2884,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize) { html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); } /* @@ -2886,7 +2901,7 @@ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize) { html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); html->InFlags |= IN_PRE; } @@ -3218,7 +3233,7 @@ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_close_par(DilloHtml *html) { - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (9, html->wordStyle ()); } /* @@ -3226,7 +3241,7 @@ static void Html_tag_close_par(DilloHtml *html) */ static void Html_tag_content_wbr(DilloHtml *html, const char *tag, int tagsize) { - HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), true); + HT2TB(html)->addBreakOption(html->wordStyle (), true); } @@ -3661,8 +3676,8 @@ static void Html_display_block(DilloHtml *html) static void Html_display_listitem(DilloHtml *html) { - Style *style = html->styleEngine->style (); - Style *wordStyle = html->styleEngine->wordStyle (); + Style *style = html->style (); + Style *wordStyle = html->wordStyle (); Widget **ref_list_item; ListItem *list_item; int *list_number; @@ -3746,7 +3761,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) /* Push the tag into the stack */ Html_push_tag(html, ni); - html->styleEngine->startElement (ni); + html->startElement (ni); _MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name); /* Parse attributes that can appear on any tag */ @@ -3757,7 +3772,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) Tags[ni].open (html, tag, tagsize); if (! S_TOP(html)->display_none) { - switch (html->styleEngine->style ()->display) { + switch (html->style ()->display) { case DISPLAY_BLOCK: Html_display_block(html); break; @@ -3784,8 +3799,8 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) if (S_TOP(html)->parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) { /* don't change anything */ } else if (S_TOP(html)->parse_mode != DILLO_HTML_PARSE_MODE_PRE && - (html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE || - html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) { + (html->style ()->whiteSpace == WHITE_SPACE_PRE || + html->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) { S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE; html->pre_column = 0; html->PreFirstChar = true; diff --git a/src/html_common.hh b/src/html_common.hh index 6c5d4807..a43d91b7 100644 --- a/src/html_common.hh +++ b/src/html_common.hh @@ -218,6 +218,22 @@ public: bool_t unloadedImages(); void loadImages (const DilloUrl *pattern); void addCssUrl(const DilloUrl *url); + + // useful shortcuts + inline void startElement (int tag) + { styleEngine->startElement (tag, bw, base_url); } + inline void startElement (const char *tagname) + { styleEngine->startElement (tagname, bw, base_url); } + + inline dw::core::style::Style *backgroundStyle () + { return styleEngine->backgroundStyle (bw, base_url); } + inline dw::core::style::Style *style () + { return styleEngine->style (bw, base_url); } + inline dw::core::style::Style *wordStyle () + { return styleEngine->wordStyle (bw, base_url); } + + inline void restyle () { styleEngine->restyle (bw, base_url); } + }; /* diff --git a/src/image.cc b/src/image.cc index c499d977..04d89a82 100644 --- a/src/image.cc +++ b/src/image.cc @@ -23,19 +23,20 @@ using namespace dw::core; -// Image to Object-Image macro -#define I2DW(Image) ((dw::Image*)(Image->dw)) +// Image to Object-ImgRenderer macro +#define I2IR(Image) ((dw::core::ImgRenderer*)(Image->img_rnd)) /* * Create and initialize a new image structure. */ -DilloImage *a_Image_new(const char *alt_text, int32_t bg_color) +DilloImage *a_Image_new(void *layout, void *img_rnd, int32_t bg_color) { DilloImage *Image; Image = dNew(DilloImage, 1); - Image->dw = (void*) new dw::Image(alt_text); + Image->layout = layout; + Image->img_rnd = img_rnd; Image->width = 0; Image->height = 0; Image->bg_color = bg_color; @@ -88,7 +89,7 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url, _MSG("a_Image_set_parms: width=%d height=%d\n", width, height); bool resize = (Image->width != width || Image->height != height); - I2DW(Image)->setBuffer((Imgbuf*)v_imgbuf, resize); + I2IR(Image)->setBuffer((Imgbuf*)v_imgbuf, resize); if (!Image->BitVec) Image->BitVec = a_Bitvec_new(height); @@ -106,7 +107,7 @@ void a_Image_write(DilloImage *Image, uint_t y) dReturn_if_fail ( y < Image->height ); /* Update the row in DwImage */ - I2DW(Image)->drawRow(y); + I2IR(Image)->drawRow(y); a_Bitvec_set_bit(Image->BitVec, y); Image->State = IMG_Write; } @@ -117,5 +118,15 @@ void a_Image_write(DilloImage *Image, uint_t y) void a_Image_close(DilloImage *Image) { _MSG("a_Image_close\n"); + I2IR(Image)->finish(); +} + +/* + * Implement the abort method + */ +void a_Image_abort(DilloImage *Image) +{ + _MSG("a_Image_abort\n"); + I2IR(Image)->fatal(); } diff --git a/src/image.hh b/src/image.hh index a66edaae..fd105a7e 100644 --- a/src/image.hh +++ b/src/image.hh @@ -44,7 +44,7 @@ typedef enum { } ImageState; struct _DilloImage { - void *dw; + void *layout, *img_rnd; /* Parameters as told by image data */ uint_t width; @@ -62,7 +62,7 @@ struct _DilloImage { /* * Function prototypes */ -DilloImage *a_Image_new(const char *alt_text, int32_t bg_color); +DilloImage *a_Image_new(void *layout, void *img_rnd, int32_t bg_color); void a_Image_ref(DilloImage *Image); void a_Image_unref(DilloImage *Image); @@ -71,6 +71,7 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url, DilloImgType type); void a_Image_write(DilloImage *Image, uint_t y); void a_Image_close(DilloImage *Image); +void a_Image_abort(DilloImage *Image); #ifdef __cplusplus diff --git a/src/imgbuf.cc b/src/imgbuf.cc index 16eb5c31..48e6bde5 100644 --- a/src/imgbuf.cc +++ b/src/imgbuf.cc @@ -90,10 +90,9 @@ void a_Imgbuf_unref(void *v_imgbuf) /* * Create a new Imgbuf */ -void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height, +void *a_Imgbuf_new(void *layout, int img_type, uint_t width, uint_t height, double gamma) { - Layout *layout = ((Widget*)v_dw)->getLayout(); if (!layout) { MSG_ERR("a_Imgbuf_new: layout is NULL.\n"); exit(1); @@ -104,7 +103,8 @@ void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height, linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size); } - return (void*)layout->createImgbuf(Imgbuf::RGB, width, height, gamma); + return (void*)((Layout*)layout)->createImgbuf(Imgbuf::RGB, width, height, + gamma); } /* diff --git a/src/imgbuf.hh b/src/imgbuf.hh index af0bf9a6..efa1b48c 100644 --- a/src/imgbuf.hh +++ b/src/imgbuf.hh @@ -16,7 +16,7 @@ extern "C" { */ void a_Imgbuf_ref(void *v_imgbuf); void a_Imgbuf_unref(void *v_imgbuf); -void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height, +void *a_Imgbuf_new(void *v_ir, int img_type, uint_t width, uint_t height, double gamma); int a_Imgbuf_last_reference(void *v_imgbuf); void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type, diff --git a/src/plain.cc b/src/plain.cc index d63ce6cf..d09f6d79 100644 --- a/src/plain.cc +++ b/src/plain.cc @@ -97,9 +97,10 @@ DilloPlain::DilloPlain(BrowserWindow *p_bw) Layout *layout = (Layout*) bw->render_layout; StyleEngine styleEngine (layout); - styleEngine.startElement ("body"); - styleEngine.startElement ("pre"); - widgetStyle = styleEngine.wordStyle (); + // TODO (3x) No URL? + styleEngine.startElement ("body", bw, NULL); + styleEngine.startElement ("pre", bw, NULL); + widgetStyle = styleEngine.wordStyle (bw, NULL); widgetStyle->ref (); /* The context menu */ diff --git a/src/styleengine.cc b/src/styleengine.cc index 47bf6240..5b3b34f0 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -15,10 +15,49 @@ #include "misc.h" #include "html_common.hh" #include "styleengine.hh" +#include "web.hh" +#include "capi.h" using namespace lout::misc; using namespace dw::core::style; +/** + * Signal handler for "delete": This handles the case when an instance + * of StyleImage is deleted, possibly when the cache client is still + * active. + * + * \todo Not neccessary for dw::Image? (dw::Image also implements + * lout::signal::ObservedObject.) + */ +class StyleImageDeletionReceiver: + public lout::signal::ObservedObject::DeletionReceiver +{ + int clientKey; + +public: + StyleImageDeletionReceiver (int clientKey); + ~StyleImageDeletionReceiver (); + + void deleted (lout::signal::ObservedObject *object); +}; + +StyleImageDeletionReceiver::StyleImageDeletionReceiver (int clientKey) +{ + this->clientKey = clientKey; +} + +StyleImageDeletionReceiver::~StyleImageDeletionReceiver () +{ +} + +void StyleImageDeletionReceiver::deleted (lout::signal::ObservedObject *object) +{ + a_Capi_stop_client (clientKey, 0); + delete this; +} + +// ---------------------------------------------------------------------- + StyleEngine::StyleEngine (dw::core::Layout *layout) { StyleAttrs style_attrs; FontAttrs font_attrs; @@ -92,8 +131,8 @@ void StyleEngine::stackPop () { /** * \brief tell the styleEngine that a new html element has started. */ -void StyleEngine::startElement (int element) { - style (); // ensure that style of current node is computed +void StyleEngine::startElement (int element, BrowserWindow *bw, DilloUrl *url) { + style (bw, url); // ensure that style of current node is computed stackPush (); Node *n = stack->getLastRef (); @@ -103,8 +142,9 @@ void StyleEngine::startElement (int element) { n->doctreeNode = dn; } -void StyleEngine::startElement (const char *tagname) { - startElement (a_Html_tag_index (tagname)); +void StyleEngine::startElement (const char *tagname, BrowserWindow *bw, + DilloUrl *url) { + startElement (a_Html_tag_index (tagname), bw, url); } void StyleEngine::setId (const char *id) { @@ -207,6 +247,26 @@ dw::core::style::Color *StyleEngine::backgroundColor () { return NULL; } +dw::core::style::StyleImage *StyleEngine::backgroundImage + (dw::core::style::BackgroundRepeat *bgRepeat, + dw::core::style::BackgroundAttachment *bgAttachment, + dw::core::style::Length *bgPositionX, + dw::core::style::Length *bgPositionY) { + for (int i = 1; i < stack->size (); i++) { + Node *n = stack->getRef (i); + + if (n->style && n->style->backgroundImage) { + *bgRepeat = n->style->backgroundRepeat; + *bgAttachment = n->style->backgroundAttachment; + *bgPositionX = n->style->backgroundPositionX; + *bgPositionY = n->style->backgroundPositionY; + return n->style->backgroundImage; + } + } + + return NULL; +} + /** * \brief set the CSS pseudo class :link. */ @@ -238,6 +298,16 @@ void StyleEngine::preprocessAttrs (dw::core::style::StyleAttrs *attrs) { if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) { attrs->backgroundColor = stack->getRef (stack->size () - 2)->style->backgroundColor; + attrs->backgroundImage = + stack->getRef (stack->size () - 2)->style->backgroundImage; + attrs->backgroundRepeat = + stack->getRef (stack->size () - 2)->style->backgroundRepeat; + attrs->backgroundAttachment = + stack->getRef (stack->size () - 2)->style->backgroundAttachment; + attrs->backgroundPositionX = + stack->getRef (stack->size () - 2)->style->backgroundPositionX; + attrs->backgroundPositionY = + stack->getRef (stack->size () - 2)->style->backgroundPositionY; attrs->valign = stack->getRef (stack->size () - 2)->style->valign; } @@ -281,7 +351,8 @@ void StyleEngine::postprocessAttrs (dw::core::style::StyleAttrs *attrs) { /** * \brief Make changes to StyleAttrs attrs according to CssPropertyList props. */ -void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { +void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, + BrowserWindow *bw, DilloUrl *url) { FontAttrs fontAttrs = *attrs->font; Font *parentFont = stack->get (i - 1).style->font; char *c, *fontName; @@ -441,6 +512,10 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { switch (p->name) { /* \todo missing cases */ + case CSS_PROPERTY_BACKGROUND_ATTACHMENT: + attrs->backgroundAttachment = + (BackgroundAttachment) p->value.intVal; + break; case CSS_PROPERTY_BACKGROUND_COLOR: if (prefs.allow_white_bg || p->value.intVal != 0xffffff) attrs->backgroundColor = Color::create(layout, p->value.intVal); @@ -448,6 +523,41 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { attrs->backgroundColor = Color::create(layout, prefs.white_bg_replacement); break; + case CSS_PROPERTY_BACKGROUND_IMAGE: + { + DilloUrl *imgUrl = + a_Url_new (p->value.strVal, a_Url_str (url)); + + attrs->backgroundImage = StyleImage::create(); + DilloImage *image = + a_Image_new(layout, + (void*)attrs->backgroundImage + ->getMainImgRenderer(), + 0xffffff); + + DilloWeb *web = a_Web_new(bw, imgUrl, url); + web->Image = image; + a_Image_ref(image); + web->flags |= WEB_Image; + + int clientKey; + if ((clientKey = a_Capi_open_url(web, NULL, NULL)) != 0) { + a_Bw_add_client(bw, clientKey, 0); + a_Bw_add_url(bw, url); + attrs->backgroundImage->connectDeletion + (new StyleImageDeletionReceiver (clientKey)); + } + } + break; + case CSS_PROPERTY_BACKGROUND_POSITION: + computeLength (&attrs->backgroundPositionX, p->value.posVal->posX, + attrs->font); + computeLength (&attrs->backgroundPositionY, p->value.posVal->posY, + attrs->font); + break; + case CSS_PROPERTY_BACKGROUND_REPEAT: + attrs->backgroundRepeat = (BackgroundRepeat) p->value.intVal; + break; case CSS_PROPERTY_BORDER_COLLAPSE: attrs->borderCollapse = (BorderCollapse) p->value.intVal; break; @@ -706,9 +816,9 @@ void StyleEngine::computeBorderWidth (int *dest, CssProperty *p, * A normal style might have backgroundColor == NULL to indicate a transparent * background. This method ensures that backgroundColor is set. */ -Style * StyleEngine::backgroundStyle () { +Style * StyleEngine::backgroundStyle (BrowserWindow *bw, DilloUrl *url) { if (!stack->getRef (stack->size () - 1)->backgroundStyle) { - StyleAttrs attrs = *style (); + StyleAttrs attrs = *style (bw, url); for (int i = stack->size () - 1; i >= 0 && ! attrs.backgroundColor; i--) attrs.backgroundColor = stack->getRef (i)->style->backgroundColor; @@ -725,7 +835,7 @@ Style * StyleEngine::backgroundStyle () { * HTML elements and the nonCssProperties that have been set. * This method is private. Call style() to get a current style object. */ -Style * StyleEngine::style0 (int i) { +Style * StyleEngine::style0 (int i, BrowserWindow *bw, DilloUrl *url) { CssPropertyList props, *styleAttrProperties, *styleAttrPropertiesImportant; CssPropertyList *nonCssProperties; // get previous style from the stack @@ -753,7 +863,7 @@ Style * StyleEngine::style0 (int i) { nonCssProperties); // apply style - apply (i, &attrs, &props); + apply (i, &attrs, &props, bw, url); postprocessAttrs (&attrs); @@ -762,14 +872,20 @@ Style * StyleEngine::style0 (int i) { return stack->getRef (i)->style; } -Style * StyleEngine::wordStyle0 () { - StyleAttrs attrs = *style (); +Style * StyleEngine::wordStyle0 (BrowserWindow *bw, DilloUrl *url) { + StyleAttrs attrs = *style (bw, url); attrs.resetValues (); - if (stack->getRef (stack->size() - 1)->inheritBackgroundColor) - attrs.backgroundColor = style ()->backgroundColor; + if (stack->getRef (stack->size() - 1)->inheritBackgroundColor) { + attrs.backgroundColor = style (bw, url)->backgroundColor; + attrs.backgroundImage = style (bw, url)->backgroundImage; + attrs.backgroundRepeat = style (bw, url)->backgroundRepeat; + attrs.backgroundAttachment = style (bw, url)->backgroundAttachment; + attrs.backgroundPositionX = style (bw, url)->backgroundPositionX; + attrs.backgroundPositionY = style (bw, url)->backgroundPositionY; + } - attrs.valign = style ()->valign; + attrs.valign = style (bw, url)->valign; stack->getRef(stack->size() - 1)->wordStyle = Style::create(&attrs); return stack->getRef (stack->size () - 1)->wordStyle; @@ -782,7 +898,7 @@ Style * StyleEngine::wordStyle0 () { * and thereby after the HTML-element has been opened. * Note that restyle() does not change any styles in the widget tree. */ -void StyleEngine::restyle () { +void StyleEngine::restyle (BrowserWindow *bw, DilloUrl *url) { for (int i = 1; i < stack->size (); i++) { Node *n = stack->getRef (i); if (n->style) { @@ -798,7 +914,7 @@ void StyleEngine::restyle () { n->backgroundStyle = NULL; } - style0 (i); + style0 (i, bw, url); } } diff --git a/src/styleengine.hh b/src/styleengine.hh index 237008a6..714553ff 100644 --- a/src/styleengine.hh +++ b/src/styleengine.hh @@ -40,8 +40,8 @@ class StyleEngine { void stackPop (); void buildUserAgentStyle (); void buildUserStyle (); - dw::core::style::Style *style0 (int i); - dw::core::style::Style *wordStyle0 (); + dw::core::style::Style *style0 (int i, BrowserWindow *bw, DilloUrl *url); + dw::core::style::Style *wordStyle0 (BrowserWindow *bw, DilloUrl *url); inline void setNonCssHint(CssPropertyName name, CssValueType type, CssPropertyValue value) { Node *n = stack->getRef (stack->size () - 1); @@ -52,7 +52,8 @@ class StyleEngine { } void preprocessAttrs (dw::core::style::StyleAttrs *attrs); void postprocessAttrs (dw::core::style::StyleAttrs *attrs); - void apply (int i, dw::core::style::StyleAttrs *attrs, CssPropertyList *props); + void apply (int i, dw::core::style::StyleAttrs *attrs, + CssPropertyList *props, BrowserWindow *bw, DilloUrl *url); bool computeValue (int *dest, CssLength value, dw::core::style::Font *font); bool computeValue (int *dest, CssLength value, @@ -68,8 +69,8 @@ class StyleEngine { void parse (DilloHtml *html, DilloUrl *url, const char *buf, int buflen, CssOrigin origin); - void startElement (int tag); - void startElement (const char *tagname); + void startElement (int tag, BrowserWindow *bw, DilloUrl *url); + void startElement (const char *tagname, BrowserWindow *bw, DilloUrl *url); void setId (const char *id); const char * getId () { return doctree->top ()->id; }; void setClass (const char *klass); @@ -91,25 +92,32 @@ class StyleEngine { } void inheritNonCssHints (); void clearNonCssHints (); - void restyle (); + void restyle (BrowserWindow *bw, DilloUrl *url); void inheritBackgroundColor (); /* \todo get rid of this somehow */ - dw::core::style::Style *backgroundStyle (); + dw::core::style::Style *backgroundStyle (BrowserWindow *bw, + DilloUrl *url); dw::core::style::Color *backgroundColor (); + dw::core::style::StyleImage *backgroundImage + (dw::core::style::BackgroundRepeat *bgRepeat, + dw::core::style::BackgroundAttachment *bgAttachment, + dw::core::style::Length *bgPositionX, + dw::core::style::Length *bgPositionY); - inline dw::core::style::Style *style () { + inline dw::core::style::Style *style (BrowserWindow *bw, DilloUrl *url) { dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style; if (s) return s; else - return style0 (stack->size () - 1); + return style0 (stack->size () - 1, bw, url); }; - inline dw::core::style::Style *wordStyle () { + inline dw::core::style::Style *wordStyle (BrowserWindow *bw, + DilloUrl *url) { dw::core::style::Style *s = stack->getRef(stack->size()-1)->wordStyle; if (s) return s; else - return wordStyle0 (); + return wordStyle0 (bw, url); }; }; diff --git a/src/table.cc b/src/table.cc index 15200516..a3002ebf 100644 --- a/src/table.cc +++ b/src/table.cc @@ -114,7 +114,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) BUG_MSG("<table> bgcolor attribute is obsolete.\n"); } - html->styleEngine->style (); // evaluate now, so we can build non-css hints for the cells + html->style (); // evaluate now, so we can build non-css hints for the cells /* The style for the cells */ html->styleEngine->clearNonCssHints (); @@ -155,10 +155,10 @@ void Html_tag_content_table(DilloHtml *html, const char *tag, int tagsize) { dw::core::Widget *table; - HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); + HT2TB(html)->addParbreak (0, html->wordStyle ()); table = new dw::Table(prefs.limit_text_width); - HT2TB(html)->addWidget (table, html->styleEngine->style ()); - HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); + HT2TB(html)->addWidget (table, html->style ()); + HT2TB(html)->addParbreak (0, html->wordStyle ()); S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP; S_TOP(html)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE; @@ -221,7 +221,7 @@ void Html_tag_content_tr(DilloHtml *html, const char *tag, int tagsize) case DILLO_HTML_TABLE_MODE_TOP: case DILLO_HTML_TABLE_MODE_TR: case DILLO_HTML_TABLE_MODE_TD: - ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ()); + ((dw::Table*)S_TOP(html)->table)->addRow (html->style ()); default: break; } @@ -302,10 +302,10 @@ static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb) int borderWidth, marginWidth; tableStyle = ((dw::Table*)S_TOP(html)->table)->getStyle (); - borderWidth = html->styleEngine->style ()->borderWidth.top; + borderWidth = html->style ()->borderWidth.top; marginWidth = tableStyle->margin.top; - collapseCellAttrs = *(html->styleEngine->style ()); + collapseCellAttrs = *(html->style ()); collapseCellAttrs.margin.setVal (0); collapseCellAttrs.borderWidth.left = 0; collapseCellAttrs.borderWidth.top = 0; @@ -344,7 +344,7 @@ static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb) dw::core::style::Style *separateStyle; dw::core::style::StyleAttrs separateCellAttrs; - separateCellAttrs = *(html->styleEngine->style ()); + separateCellAttrs = *(html->style ()); /* CSS2 17.5: Internal table elements do not have margins */ separateCellAttrs.margin.setVal (0); separateStyle = Style::create(&separateCellAttrs); @@ -443,7 +443,7 @@ static void Html_tag_content_table_cell(DilloHtml *html, /* TODO: check errors? */ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan"))) rowspan = MAX(1, strtol (attrbuf, NULL, 10)); - if (html->styleEngine->style ()->textAlign + if (html->style ()->textAlign == TEXT_ALIGN_STRING) col_tb = new dw::TableCell ( ((dw::Table*)S_TOP(html)->table)->getCellRef (), @@ -451,7 +451,7 @@ static void Html_tag_content_table_cell(DilloHtml *html, else col_tb = new Textblock (prefs.limit_text_width); - if (html->styleEngine->style()->borderCollapse == BORDER_MODEL_COLLAPSE){ + if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){ Html_set_collapsing_border_model(html, col_tb); } else { Html_set_separate_border_model(html, col_tb); diff --git a/src/uicmd.cc b/src/uicmd.cc index 84e7e4cd..4f737255 100644 --- a/src/uicmd.cc +++ b/src/uicmd.cc @@ -583,6 +583,9 @@ static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus) Layout *layout = new Layout (platform); style::Color *bgColor = style::Color::create (layout, prefs.bg_color); layout->setBgColor (bgColor); + layout->setBgImage (NULL, style::BACKGROUND_REPEAT, + style::BACKGROUND_ATTACHMENT_SCROLL, + style::createPerLength (0), style::createPerLength (0)); // set_render_layout() sets the proper viewport size FltkViewport *viewport = new FltkViewport (0, 0, 0, 1); @@ -67,16 +67,20 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web, style::Color *bgColor = style::Color::create (layout, prefs.bg_color); Web->bgColor = bgColor->getColor (); layout->setBgColor (bgColor); + layout->setBgImage (NULL, style::BACKGROUND_REPEAT, + style::BACKGROUND_ATTACHMENT_SCROLL, + style::createPerLength (0), + style::createPerLength (0)); /* Set a style for the widget */ StyleEngine styleEngine (layout); - styleEngine.startElement ("body"); + styleEngine.startElement ("body", Web->bw, Web->url); dw = (Widget*) viewer(Type, Web, Call, Data); if (dw == NULL) return -1; - dw->setStyle (styleEngine.style ()); + dw->setStyle (styleEngine.style (Web->bw, Web->url)); /* This method frees the old dw if any */ layout->setWidget(dw); diff --git a/test/Makefile.am b/test/Makefile.am index 6a834acb..156c8667 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -9,6 +9,7 @@ noinst_PROGRAMS = \ dw-find-test \ dw-links \ dw-links2 \ + dw-image-background \ dw-images-simple \ dw-images-scaled \ dw-images-scaled2 \ @@ -67,6 +68,14 @@ dw_links2_LDADD = \ $(top_builddir)/lout/liblout.a \ @LIBFLTK_LIBS@ +dw_image_background_SOURCES = dw_image_background.cc +dw_image_background_LDADD = \ + $(top_builddir)/dw/libDw-widgets.a \ + $(top_builddir)/dw/libDw-fltk.a \ + $(top_builddir)/dw/libDw-core.a \ + $(top_builddir)/lout/liblout.a \ + @LIBFLTK_LIBS@ + dw_images_simple_SOURCES = dw_images_simple.cc dw_images_simple_LDADD = \ $(top_builddir)/dw/libDw-widgets.a \ diff --git a/test/dw_image_background.cc b/test/dw_image_background.cc new file mode 100644 index 00000000..24966850 --- /dev/null +++ b/test/dw_image_background.cc @@ -0,0 +1,196 @@ +/* + * Dillo Widget + * + * Copyright 2013 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#include <FL/Fl.H> +#include <FL/Fl_Window.H> + +#include "../dw/core.hh" +#include "../dw/fltkcore.hh" +#include "../dw/fltkviewport.hh" +#include "../dw/textblock.hh" +#include "../dw/image.hh" + +using namespace lout::signal; +using namespace lout::misc; +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +class ImageStyleDeletionReceiver: public ObservedObject::DeletionReceiver +{ +public: + void deleted (ObservedObject *object); +}; + +static StyleImage *image1 = NULL, *image2 = NULL; +static Layout *layout; +static int imgRow1 = 0, imgRow2 = 0; +static ImageStyleDeletionReceiver isdr; + +void ImageStyleDeletionReceiver::deleted (ObservedObject *object) +{ + if ((StyleImage*)object == image1) + image1 = NULL; + else if ((StyleImage*)object == image2) + image2 = NULL; + else + assertNotReached (); +} + +static void imageInitTimeout (void *data) +{ + if (image1) { + Imgbuf *imgbuf1 = layout->createImgbuf (Imgbuf::RGB, 400, 200, 1); + image1->getMainImgRenderer()->setBuffer (imgbuf1, false); + } + + if (image2) { + Imgbuf *imgbuf2 = layout->createImgbuf (Imgbuf::RGB, 100, 100, 1); + image2->getMainImgRenderer()->setBuffer (imgbuf2, false); + } +} + +static void imageDrawTimeout (void *data) +{ + Imgbuf *imgbuf1 = image1 ? image1->getImgbufSrc () : NULL; + Imgbuf *imgbuf2 = image2 ? image2->getImgbufSrc () : NULL; + + if (imgbuf1 && imgRow1 < 200) { + byte buf[3 * 400]; + for(int x = 0; x < 400; x++) { + buf[3 * x + 0] = 128 + x * 127 / 399; + buf[3 * x + 1] = 128 + (399 - x) * 127 / 399; + buf[3 * x + 2] = 128 + imgRow1 * 127 / 199; + } + + imgbuf1->copyRow (imgRow1, buf); + image1->getMainImgRenderer()->drawRow (imgRow1); + imgRow1++; + } + + if (imgbuf2 && imgRow2 < 100) { + byte buf[3 * 100]; + for(int x = 0; x < 100; x++) { + int r = 128 + rand () % 127; + buf[3 * x + 0] = buf[3 * x + 1] = buf[3 * x + 2] = r; + } + + imgbuf2->copyRow (imgRow2, buf); + image2->getMainImgRenderer()->drawRow (imgRow2); + imgRow2++; + } + + if(imgRow1 < 200 || imgRow2 < 100) + Fl::repeat_timeout (0.5, imageDrawTimeout, NULL); +} + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(200, 300, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 200, 300); + layout->attachView (viewport); + + image1 = StyleImage::create (); + image1->connectDeletion (&isdr); + layout->setBgImage (image1, BACKGROUND_REPEAT_Y, + BACKGROUND_ATTACHMENT_SCROLL, createPerLength (0.5), + createAbsLength (30)); + + StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); + styleAttrs.x_lang[0] = 'e'; + styleAttrs.x_lang[1] = 'n'; + + FontAttrs fontAttrs; + fontAttrs.name = "Bitstream Charter"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + //styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *widgetStyle = Style::create (&styleAttrs); + + Textblock *textblock = new Textblock (false); + textblock->setStyle (widgetStyle); + layout->setWidget (textblock); + + widgetStyle->unref(); + + styleAttrs.margin.setVal (0); + styleAttrs.backgroundColor = NULL; + styleAttrs.backgroundImage = NULL; + + Style *wordStyle = Style::create (&styleAttrs); + + image2 = styleAttrs.backgroundImage = StyleImage::create (); + image2->connectDeletion (&isdr); + styleAttrs.backgroundRepeat = BACKGROUND_REPEAT; + styleAttrs.backgroundPositionX = createPerLength (0); + styleAttrs.backgroundPositionY = createPerLength (0); + Style *wordStyleBg = Style::create (&styleAttrs); + + for(int i = 1; i <= 1; i++) { + char buf[4]; + sprintf(buf, "%d.", i); + + const char *words[] = { "This", "is", "the", buf, "paragraph.", + "Here", "comes", "some", "more", "text", + "to", "demonstrate", "word", "wrapping.", + NULL }; + + for(int j = 0; words[j]; j++) { + textblock->addText(words[j], j == 11 ? wordStyleBg : wordStyle); + textblock->addSpace(wordStyle); + } + + textblock->addParbreak(10, wordStyle); + } + + wordStyle->unref(); + wordStyleBg->unref(); + + textblock->flush (); + + window->resizable(viewport); + window->show(); + + Fl::add_timeout (1.0, imageInitTimeout, NULL); + Fl::add_timeout (0.1, imageDrawTimeout, NULL); + + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} |