diff options
Diffstat (limited to 'dw/style.cc')
-rw-r--r-- | dw/style.cc | 465 |
1 files changed, 443 insertions, 22 deletions
diff --git a/dw/style.cc b/dw/style.cc index 51dda098..3f39a39e 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -32,6 +32,27 @@ namespace dw { namespace core { namespace style { +const bool drawBackgroundLineByLine = false; + +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; @@ -46,6 +67,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; vloat = FLOAT_NONE; clear = CLEAR_NONE; @@ -83,6 +109,11 @@ void StyleAttrs::resetValues () top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check specification. */ backgroundColor = NULL; + backgroundImage = NULL; + backgroundRepeat = BACKGROUND_REPEAT; + backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL; + backgroundPositionX = createPerLength (0); + backgroundPositionY = createPerLength (0); width = LENGTH_AUTO; height = LENGTH_AUTO; @@ -123,6 +154,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 && @@ -170,6 +206,11 @@ int StyleAttrs::hashValue () { textDecoration + (intptr_t) color + (intptr_t) backgroundColor + + (intptr_t) backgroundImage + + backgroundRepeat + + backgroundAttachment + + backgroundPositionX + + backgroundPositionY + textAlign + valign + textAlignChar + @@ -226,6 +267,8 @@ Style::Style (StyleAttrs *attrs) color->ref (); if (backgroundColor) backgroundColor->ref (); + if (backgroundImage) + backgroundImage->ref (); if (borderColor.top) borderColor.top->ref(); if (borderColor.bottom) @@ -248,6 +291,8 @@ Style::~Style () color->unref (); if (backgroundColor) backgroundColor->unref (); + if (backgroundImage) + backgroundImage->unref (); if (borderColor.top) borderColor.top->unref(); if (borderColor.bottom) @@ -269,6 +314,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; @@ -450,6 +500,210 @@ 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. + // + // This "tiled" buffer is not used when 'background-repeat' has + // another value than 'repeat', for obvious reasons. Image + // buffers only "tiled" in one dimension (to optimize 'repeat-x' + // and 'repeat-y') are not supported. + + 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 + // horizontally. + 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. @@ -851,8 +1105,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) { @@ -889,29 +1146,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 background 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(repeatX, repeatY); + int imgWidthT = imgbufT->getRootWidth (); + int imgHeightT = imgbufT->getRootHeight (); + int tilesX = backgroundImage->getTilesX (repeatX, repeatY); + int tilesY = backgroundImage->getTilesY (repeatX, repeatY); + + 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; } } |