1481 lines
50 KiB
C++
1481 lines
50 KiB
C++
static_assert( __cplusplus > 2020'02 );
|
|
/*
|
|
* Dillo Widget
|
|
*
|
|
* Copyright 2005-2007 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "dlib/dlib.hh"
|
|
#include "core.hh"
|
|
#include "../lout/msg.h"
|
|
|
|
using namespace lout;
|
|
|
|
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;
|
|
x_lang[0] = x_lang[1] = 0;
|
|
x_img = -1;
|
|
x_tooltip = NULL;
|
|
textDecoration = TEXT_DECORATION_NONE;
|
|
textAlign = TEXT_ALIGN_LEFT;
|
|
textAlignChar = '.';
|
|
textTransform = TEXT_TRANSFORM_NONE;
|
|
listStylePosition = LIST_STYLE_POSITION_OUTSIDE;
|
|
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;
|
|
minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO;
|
|
vloat = FLOAT_NONE;
|
|
clear = CLEAR_NONE;
|
|
overflow = OVERFLOW_VISIBLE;
|
|
position = POSITION_STATIC;
|
|
top = bottom = left = right = LENGTH_AUTO;
|
|
textIndent = 0;
|
|
margin.setVal (0);
|
|
borderWidth.setVal (0);
|
|
padding.setVal (0);
|
|
borderCollapse = BORDER_MODEL_SEPARATE;
|
|
setBorderColor (NULL);
|
|
setBorderStyle (BORDER_NONE);
|
|
hBorderSpacing = 0;
|
|
vBorderSpacing = 0;
|
|
wordSpacing = 0;
|
|
|
|
display = DISPLAY_INLINE;
|
|
whiteSpace = WHITE_SPACE_NORMAL;
|
|
cursor = CURSOR_DEFAULT;
|
|
zIndex = Z_INDEX_AUTO;
|
|
}
|
|
|
|
/**
|
|
* \brief Reset those style attributes to their standard values, which are
|
|
* not inherited, according to CSS.
|
|
*/
|
|
void StyleAttrs::resetValues ()
|
|
{
|
|
x_img = -1;
|
|
|
|
valign = VALIGN_BASELINE;
|
|
textAlignChar = '.';
|
|
vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
|
|
clear = CLEAR_NONE; /** \todo Correct? Check specification. */
|
|
overflow = OVERFLOW_VISIBLE;
|
|
position = POSITION_STATIC; /** \todo Correct? Check specification. */
|
|
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;
|
|
minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO;
|
|
|
|
margin.setVal (0);
|
|
borderWidth.setVal (0);
|
|
padding.setVal (0);
|
|
setBorderColor (NULL);
|
|
setBorderStyle (BORDER_NONE);
|
|
hBorderSpacing = 0;
|
|
vBorderSpacing = 0;
|
|
|
|
display = DISPLAY_INLINE;
|
|
}
|
|
|
|
/**
|
|
* \brief This method returns whether something may change its size, when
|
|
* its style changes from this style to \em otherStyle.
|
|
*
|
|
* It is mainly for optimizing style changes where only colors etc change
|
|
* (where false would be returned), in some cases it may return true, although
|
|
* a size change does not actually happen (e.g. when in a certain
|
|
* context a particular attribute is ignored).
|
|
*
|
|
* \todo Should for CSS implemented properly. Currently, size changes are
|
|
* not needed, so always false is returned. See also
|
|
* dw::core::Widget::setStyle.
|
|
*/
|
|
bool StyleAttrs::sizeDiffs (StyleAttrs *otherStyle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool StyleAttrs::equals (const object::Object *other) const {
|
|
StyleAttrs *otherAttrs = (StyleAttrs *) other;
|
|
|
|
return this == otherAttrs ||
|
|
(font == otherAttrs->font &&
|
|
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 &&
|
|
textTransform == otherAttrs->textTransform &&
|
|
vloat == otherAttrs->vloat &&
|
|
clear == otherAttrs->clear &&
|
|
overflow == otherAttrs->overflow &&
|
|
position == otherAttrs->position &&
|
|
top == otherAttrs->top &&
|
|
bottom == otherAttrs->bottom &&
|
|
left == otherAttrs->left &&
|
|
right == otherAttrs->right &&
|
|
hBorderSpacing == otherAttrs->hBorderSpacing &&
|
|
vBorderSpacing == otherAttrs->vBorderSpacing &&
|
|
wordSpacing == otherAttrs->wordSpacing &&
|
|
width == otherAttrs->width &&
|
|
height == otherAttrs->height &&
|
|
minWidth == otherAttrs->minWidth &&
|
|
maxWidth == otherAttrs->maxWidth &&
|
|
minHeight == otherAttrs->minHeight &&
|
|
maxHeight == otherAttrs->maxHeight &&
|
|
lineHeight == otherAttrs->lineHeight &&
|
|
textIndent == otherAttrs->textIndent &&
|
|
margin.equals (&otherAttrs->margin) &&
|
|
borderWidth.equals (&otherAttrs->borderWidth) &&
|
|
padding.equals (&otherAttrs->padding) &&
|
|
borderCollapse == otherAttrs->borderCollapse &&
|
|
borderColor.top == otherAttrs->borderColor.top &&
|
|
borderColor.right == otherAttrs->borderColor.right &&
|
|
borderColor.bottom == otherAttrs->borderColor.bottom &&
|
|
borderColor.left == otherAttrs->borderColor.left &&
|
|
borderStyle.top == otherAttrs->borderStyle.top &&
|
|
borderStyle.right == otherAttrs->borderStyle.right &&
|
|
borderStyle.bottom == otherAttrs->borderStyle.bottom &&
|
|
borderStyle.left == otherAttrs->borderStyle.left &&
|
|
display == otherAttrs->display &&
|
|
whiteSpace == otherAttrs->whiteSpace &&
|
|
listStylePosition == otherAttrs->listStylePosition &&
|
|
listStyleType == otherAttrs->listStyleType &&
|
|
cursor == otherAttrs->cursor &&
|
|
zIndex == otherAttrs->zIndex &&
|
|
x_link == otherAttrs->x_link &&
|
|
x_lang[0] == otherAttrs->x_lang[0] &&
|
|
x_lang[1] == otherAttrs->x_lang[1] &&
|
|
x_img == otherAttrs->x_img &&
|
|
x_tooltip == otherAttrs->x_tooltip);
|
|
}
|
|
|
|
int StyleAttrs::hashValue () {
|
|
return (intptr_t) font +
|
|
textDecoration +
|
|
(intptr_t) color +
|
|
(intptr_t) backgroundColor +
|
|
(intptr_t) backgroundImage +
|
|
backgroundRepeat +
|
|
backgroundAttachment +
|
|
backgroundPositionX +
|
|
backgroundPositionY +
|
|
textAlign +
|
|
valign +
|
|
textAlignChar +
|
|
textTransform +
|
|
vloat +
|
|
clear +
|
|
overflow +
|
|
position +
|
|
top +
|
|
bottom +
|
|
left +
|
|
right +
|
|
hBorderSpacing +
|
|
vBorderSpacing +
|
|
wordSpacing +
|
|
width +
|
|
height +
|
|
minWidth +
|
|
maxWidth +
|
|
minHeight +
|
|
maxHeight +
|
|
lineHeight +
|
|
textIndent +
|
|
margin.hashValue () +
|
|
borderWidth.hashValue () +
|
|
padding.hashValue () +
|
|
borderCollapse +
|
|
(intptr_t) borderColor.top +
|
|
(intptr_t) borderColor.right +
|
|
(intptr_t) borderColor.bottom +
|
|
(intptr_t) borderColor.left +
|
|
borderStyle.top +
|
|
borderStyle.right +
|
|
borderStyle.bottom +
|
|
borderStyle.left +
|
|
display +
|
|
whiteSpace +
|
|
listStylePosition +
|
|
listStyleType +
|
|
cursor +
|
|
zIndex +
|
|
x_link +
|
|
x_lang[0] + x_lang[1] +
|
|
x_img +
|
|
(intptr_t) x_tooltip;
|
|
}
|
|
|
|
int Style::totalRef = 0;
|
|
container::typed::HashTable <StyleAttrs, Style> * Style::styleTable =
|
|
new container::typed::HashTable <StyleAttrs, Style> (false, false, 1024);
|
|
|
|
Style::Style (StyleAttrs *attrs)
|
|
{
|
|
DBG_OBJ_CREATE ("dw::core::style::Style");
|
|
|
|
copyAttrs (attrs);
|
|
|
|
DBG_OBJ_ASSOC_CHILD (font);
|
|
DBG_OBJ_ASSOC_CHILD (color);
|
|
DBG_OBJ_ASSOC_CHILD (backgroundColor);
|
|
DBG_OBJ_ASSOC_CHILD (backgroundImage);
|
|
DBG_OBJ_ASSOC_CHILD (borderColor.top);
|
|
DBG_OBJ_ASSOC_CHILD (borderColor.bottom);
|
|
DBG_OBJ_ASSOC_CHILD (borderColor.left);
|
|
DBG_OBJ_ASSOC_CHILD (borderColor.right);
|
|
//DBG_OBJ_ASSOC_CHILD (x_tooltip);
|
|
|
|
refCount = 1;
|
|
|
|
font->ref ();
|
|
if (color)
|
|
color->ref ();
|
|
if (backgroundColor)
|
|
backgroundColor->ref ();
|
|
if (backgroundImage)
|
|
backgroundImage->ref ();
|
|
if (borderColor.top)
|
|
borderColor.top->ref();
|
|
if (borderColor.bottom)
|
|
borderColor.bottom->ref();
|
|
if (borderColor.left)
|
|
borderColor.left->ref();
|
|
if (borderColor.right)
|
|
borderColor.right->ref();
|
|
if (x_tooltip)
|
|
x_tooltip->ref();
|
|
|
|
totalRef++;
|
|
}
|
|
|
|
Style::~Style ()
|
|
{
|
|
font->unref ();
|
|
|
|
if (color)
|
|
color->unref ();
|
|
if (backgroundColor)
|
|
backgroundColor->unref ();
|
|
if (backgroundImage)
|
|
backgroundImage->unref ();
|
|
if (borderColor.top)
|
|
borderColor.top->unref();
|
|
if (borderColor.bottom)
|
|
borderColor.bottom->unref();
|
|
if (borderColor.left)
|
|
borderColor.left->unref();
|
|
if (borderColor.right)
|
|
borderColor.right->unref();
|
|
if (x_tooltip)
|
|
x_tooltip->unref();
|
|
|
|
styleTable->remove (this);
|
|
totalRef--;
|
|
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void Style::copyAttrs (StyleAttrs *attrs)
|
|
{
|
|
font = attrs->font;
|
|
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;
|
|
textTransform = attrs->textTransform;
|
|
vloat = attrs->vloat;
|
|
clear = attrs->clear;
|
|
overflow = attrs->overflow;
|
|
position = attrs->position;
|
|
top = attrs->top;
|
|
bottom = attrs->bottom;
|
|
left = attrs->left;
|
|
right = attrs->right;
|
|
hBorderSpacing = attrs->hBorderSpacing;
|
|
vBorderSpacing = attrs->vBorderSpacing;
|
|
wordSpacing = attrs->wordSpacing;
|
|
width = attrs->width;
|
|
height = attrs->height;
|
|
lineHeight = attrs->lineHeight;
|
|
textIndent = attrs->textIndent;
|
|
minWidth = attrs->minWidth;
|
|
maxWidth = attrs->maxWidth;
|
|
minHeight = attrs->minHeight;
|
|
maxHeight = attrs->maxHeight;
|
|
margin = attrs->margin;
|
|
borderWidth = attrs->borderWidth;
|
|
padding = attrs->padding;
|
|
borderCollapse = attrs->borderCollapse;
|
|
borderColor = attrs->borderColor;
|
|
borderStyle = attrs->borderStyle;
|
|
display = attrs->display;
|
|
whiteSpace = attrs->whiteSpace;
|
|
listStylePosition = attrs->listStylePosition;
|
|
listStyleType = attrs->listStyleType;
|
|
cursor = attrs->cursor;
|
|
zIndex = attrs->zIndex;
|
|
x_link = attrs->x_link;
|
|
x_lang[0] = attrs->x_lang[0];
|
|
x_lang[1] = attrs->x_lang[1];
|
|
x_img = attrs->x_img;
|
|
x_tooltip = attrs->x_tooltip;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool FontAttrs::equals(const object::Object *other) const
|
|
{
|
|
FontAttrs *otherAttrs = (FontAttrs*)other;
|
|
return
|
|
this == otherAttrs ||
|
|
(size == otherAttrs->size &&
|
|
weight == otherAttrs->weight &&
|
|
style == otherAttrs->style &&
|
|
letterSpacing == otherAttrs->letterSpacing &&
|
|
fontVariant == otherAttrs->fontVariant &&
|
|
strcmp (name, otherAttrs->name) == 0);
|
|
}
|
|
|
|
int FontAttrs::hashValue()
|
|
{
|
|
int h = object::String::hashValue (name);
|
|
h = (h << 5) - h + size;
|
|
h = (h << 5) - h + weight;
|
|
h = (h << 5) - h + style;
|
|
h = (h << 5) - h + letterSpacing;
|
|
h = (h << 5) - h + fontVariant;
|
|
return h;
|
|
}
|
|
|
|
Font::~Font ()
|
|
{
|
|
free ((char*)name);
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void Font::copyAttrs (FontAttrs *attrs)
|
|
{
|
|
name = dStrdup (attrs->name);
|
|
size = attrs->size;
|
|
weight = attrs->weight;
|
|
style = attrs->style;
|
|
letterSpacing = attrs->letterSpacing;
|
|
fontVariant = attrs->fontVariant;
|
|
}
|
|
|
|
Font *Font::create0 (Layout *layout, FontAttrs *attrs,
|
|
bool tryEverything)
|
|
{
|
|
return layout->createFont (attrs, tryEverything);
|
|
}
|
|
|
|
Font *Font::create (Layout *layout, FontAttrs *attrs)
|
|
{
|
|
return create0 (layout, attrs, false);
|
|
}
|
|
|
|
bool Font::exists (Layout *layout, const char *name)
|
|
{
|
|
return layout->fontExists (name);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool ColorAttrs::equals(const object::Object *other) const
|
|
{
|
|
ColorAttrs *oc = (ColorAttrs*)other;
|
|
return this == oc || (color == oc->color);
|
|
}
|
|
|
|
int ColorAttrs::hashValue()
|
|
{
|
|
return color;
|
|
}
|
|
|
|
Color::~Color ()
|
|
{
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
int Color::shadeColor (int color, int d)
|
|
{
|
|
int red = (color >> 16) & 255;
|
|
int green = (color >> 8) & 255;
|
|
int blue = color & 255;
|
|
|
|
double oldLightness = ( (double) std::max( red, std::max( green, blue ) ) ) / 255;
|
|
double newLightness;
|
|
|
|
if (oldLightness > 0.8) {
|
|
if (d > 0)
|
|
newLightness = oldLightness - 0.2;
|
|
else
|
|
newLightness = oldLightness - 0.4;
|
|
} else if (oldLightness < 0.2) {
|
|
if (d > 0)
|
|
newLightness = oldLightness + 0.4;
|
|
else
|
|
newLightness = oldLightness + 0.2;
|
|
} else
|
|
newLightness = oldLightness + d * 0.2;
|
|
|
|
if (oldLightness) {
|
|
double f = (newLightness / oldLightness);
|
|
red = (int)(red * f);
|
|
green = (int)(green * f);
|
|
blue = (int)(blue * f);
|
|
} else {
|
|
red = green = blue = (int)(newLightness * 255);
|
|
}
|
|
|
|
return (red << 16) | (green << 8) | blue;
|
|
}
|
|
|
|
int Color::shadeColor (int color, Shading shading)
|
|
{
|
|
switch (shading) {
|
|
case SHADING_NORMAL:
|
|
return color;
|
|
|
|
case SHADING_LIGHT:
|
|
return shadeColor(color, +1);
|
|
|
|
case SHADING_INVERSE:
|
|
return color ^ 0xffffff;
|
|
|
|
case SHADING_DARK:
|
|
return shadeColor(color, -1);
|
|
|
|
default:
|
|
// compiler happiness
|
|
misc::assertNotReached ();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
Color *Color::create (Layout *layout, int col)
|
|
{
|
|
ColorAttrs attrs(col);
|
|
|
|
return layout->createColor (col);
|
|
}
|
|
|
|
Tooltip *Tooltip::create (Layout *layout, const char *text)
|
|
{
|
|
return layout->createTooltip (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;
|
|
DBG_OBJ_ASSOC (image, image->imgbufSrc);
|
|
|
|
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 =
|
|
std::max (OPT_BG_IMG_W / image->imgbufSrc->getRootWidth(), 1);
|
|
image->tilesY =
|
|
std::max (OPT_BG_IMG_H / image->imgbufSrc->getRootHeight(), 1);
|
|
image->imgbufTiled =
|
|
image->imgbufSrc->createSimilarBuf
|
|
(image->tilesX * image->imgbufSrc->getRootWidth(),
|
|
image->tilesY * image->imgbufSrc->getRootHeight());
|
|
|
|
DBG_OBJ_ASSOC (image, image->imgbufTiled);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ()
|
|
{
|
|
DBG_OBJ_CREATE ("dw::core::style::StyleImage");
|
|
|
|
refCount = 0;
|
|
imgbufSrc = NULL;
|
|
imgbufTiled = NULL;
|
|
|
|
imgRendererDist = new ImgRendererDist ();
|
|
styleImgRenderer= std::make_unique< StyleImgRenderer >( this ).release();
|
|
imgRendererDist->put( std::unique_ptr< StyleImgRenderer >{ styleImgRenderer } );
|
|
}
|
|
|
|
StyleImage::~StyleImage ()
|
|
{
|
|
if (imgbufSrc)
|
|
imgbufSrc->unref ();
|
|
if (imgbufTiled)
|
|
imgbufTiled->unref ();
|
|
|
|
delete imgRendererDist;
|
|
delete styleImgRenderer;
|
|
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
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 = std::max (origX + tileX1 * imgWidth, x);
|
|
int x2 = std::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.
|
|
* Although the concept is simple, achieving pixel accuracy is laborious [1].
|
|
*
|
|
* [1] https://dillo-browser.github.io/old/css_compat/tests/border-style.html
|
|
*/
|
|
static void drawBorderTop(View *view, Style *style,
|
|
int x1, int y1, int x2, int y2)
|
|
|
|
{
|
|
int d, w;
|
|
Point points[4];
|
|
const bool filled = true, convex = true;
|
|
bool ridge = false, inset = false, dotted = false;
|
|
Color::Shading shading = Color::SHADING_NORMAL;
|
|
|
|
if (!style->borderColor.top || style->borderWidth.top == 0)
|
|
return;
|
|
|
|
switch (style->borderStyle.top) {
|
|
case BORDER_NONE:
|
|
case BORDER_HIDDEN:
|
|
break;
|
|
case BORDER_DOTTED:
|
|
dotted = true;
|
|
/* fallthrough */
|
|
case BORDER_DASHED:
|
|
w = style->borderWidth.top;
|
|
view->drawTypedLine(style->borderColor.top, shading,
|
|
dotted ? LINE_DOTTED : LINE_DASHED,
|
|
w, x1+w/2, y1+w/2, x2-w/2, y2+w/2);
|
|
break;
|
|
case BORDER_SOLID:
|
|
case BORDER_INSET:
|
|
inset = true;
|
|
/* fallthrough */
|
|
case BORDER_OUTSET:
|
|
if (style->borderStyle.top != BORDER_SOLID)
|
|
shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
|
|
if (style->borderWidth.top == 1) {
|
|
view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2);
|
|
} else {
|
|
points[0].x = x1;
|
|
points[1].x = x2 + 1;
|
|
points[0].y = points[1].y = y1;
|
|
points[2].x = points[1].x - style->borderWidth.right;
|
|
points[3].x = x1 + style->borderWidth.left;
|
|
points[2].y = points[3].y = points[0].y + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.top, shading, filled, convex,
|
|
points, 4);
|
|
}
|
|
break;
|
|
case BORDER_RIDGE:
|
|
ridge = true;
|
|
/* fallthrough */
|
|
case BORDER_GROOVE:
|
|
d = style->borderWidth.top & 1;
|
|
points[0].x = x1;
|
|
points[1].x = x2 + 1;
|
|
points[0].y = points[1].y = y1;
|
|
points[2].x = x2 - style->borderWidth.right / 2;
|
|
points[3].x = x1 + style->borderWidth.left / 2;
|
|
points[2].y = points[3].y = y1 + style->borderWidth.top / 2 + d;
|
|
shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
view->drawPolygon (style->borderColor.top, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = x1 + style->borderWidth.left / 2 + d;
|
|
points[1].x = x2 - style->borderWidth.right / 2 + 1 - d;
|
|
points[0].y = points[1].y = y1 + style->borderWidth.top / 2 + d;
|
|
points[2].x = x2 - style->borderWidth.right + 1 - d;
|
|
points[3].x = x1 + style->borderWidth.left;
|
|
points[2].y = points[3].y = y1 + style->borderWidth.top;
|
|
shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
view->drawPolygon (style->borderColor.top, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
case BORDER_DOUBLE:
|
|
w = (int) rint(style->borderWidth.top / 3.0);
|
|
d = w ? style->borderWidth.top - 2 * w : 0;
|
|
int w_l = (int) rint(style->borderWidth.left / 3.0);
|
|
int w_r = (int) rint(style->borderWidth.right / 3.0);
|
|
if (style->borderWidth.top == 1) {
|
|
view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2);
|
|
break;
|
|
}
|
|
points[0].x = x1;
|
|
points[1].x = x2 + 1;
|
|
points[0].y = points[1].y = y1;
|
|
points[2].x = points[1].x - w_r;
|
|
points[3].x = points[0].x + w_l;
|
|
points[2].y = points[3].y = points[0].y + w;
|
|
view->drawPolygon (style->borderColor.top, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = x1 + style->borderWidth.left - w_l;
|
|
points[1].x = x2 + 1 - style->borderWidth.right + w_r;
|
|
points[0].y = points[1].y = y1 + w + d;
|
|
points[2].x = x2 + 1 - style->borderWidth.right;
|
|
points[3].x = x1 + style->borderWidth.left;
|
|
points[2].y = points[3].y = y1 + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.top, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void drawBorderBottom(View *view, Style *style,
|
|
int x1, int y1, int x2, int y2)
|
|
|
|
{
|
|
int d, w;
|
|
Point points[4];
|
|
const bool filled = true, convex = true;
|
|
bool ridge = false, inset = false, dotted = false;
|
|
Color::Shading shading = Color::SHADING_NORMAL;
|
|
|
|
if (!style->borderColor.bottom || style->borderWidth.bottom == 0)
|
|
return;
|
|
|
|
switch (style->borderStyle.bottom) {
|
|
case BORDER_NONE:
|
|
case BORDER_HIDDEN:
|
|
break;
|
|
case BORDER_DOTTED:
|
|
dotted = true;
|
|
/* fallthrough */
|
|
case BORDER_DASHED:
|
|
w = style->borderWidth.bottom;
|
|
view->drawTypedLine(style->borderColor.bottom, shading,
|
|
dotted ? LINE_DOTTED : LINE_DASHED,
|
|
w, x1+w/2, y1-w/2, x2-w/2, y2-w/2);
|
|
break;
|
|
case BORDER_SOLID:
|
|
case BORDER_INSET:
|
|
inset = true;
|
|
/* fallthrough */
|
|
case BORDER_OUTSET:
|
|
if (style->borderStyle.bottom != BORDER_SOLID)
|
|
shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
|
|
if (style->borderWidth.bottom == 1) { /* 1 pixel line */
|
|
view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2);
|
|
} else {
|
|
points[0].x = x1 - 1;
|
|
points[1].x = x2 + 2;
|
|
points[0].y = points[1].y = y1 + 1;
|
|
points[2].x = points[1].x - style->borderWidth.right;
|
|
points[3].x = points[0].x + style->borderWidth.left;
|
|
points[2].y = points[3].y = points[0].y-style->borderWidth.bottom;
|
|
view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
|
|
points, 4);
|
|
}
|
|
break;
|
|
case BORDER_RIDGE:
|
|
ridge = true;
|
|
/* fallthrough */
|
|
case BORDER_GROOVE:
|
|
w = style->borderWidth.bottom;
|
|
d = w & 1;
|
|
points[0].x = x1 - 1;
|
|
points[1].x = x2 + 2 - d;
|
|
points[0].y = points[1].y = y1 + 1;
|
|
points[2].x = points[1].x - style->borderWidth.right / 2;
|
|
points[3].x = points[0].x + style->borderWidth.left / 2 + d;
|
|
points[2].y = points[3].y = points[0].y - w/2 - d;
|
|
shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
|
|
points, 4);
|
|
// clockwise
|
|
points[0].x = x1 + style->borderWidth.left - 1;
|
|
points[1].x = x2 + 1 - style->borderWidth.right + 1;
|
|
points[0].y = points[1].y = y1 - w + 1;
|
|
points[2].x = points[1].x + style->borderWidth.right / 2;
|
|
points[3].x = points[0].x - style->borderWidth.left / 2;
|
|
points[2].y = points[3].y = points[0].y + w/2;
|
|
shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
case BORDER_DOUBLE:
|
|
w = (int) rint(style->borderWidth.bottom / 3.0);
|
|
d = w ? style->borderWidth.bottom - 2 * w : 0;
|
|
int w_l = (int) rint(style->borderWidth.left / 3.0);
|
|
int w_r = (int) rint(style->borderWidth.right / 3.0);
|
|
if (style->borderWidth.bottom == 1) {
|
|
view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2);
|
|
break;
|
|
}
|
|
points[0].x = x2 + 2;
|
|
points[1].x = x1 - 1;
|
|
points[0].y = points[1].y = y1 + 1;
|
|
points[2].x = points[1].x + w_l;
|
|
points[3].x = points[0].x - w_r;
|
|
points[2].y = points[3].y = points[0].y - w;
|
|
view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = x2 + 2 - style->borderWidth.right + w_r;
|
|
points[1].x = x1 - 1 + style->borderWidth.left - w_l;
|
|
points[0].y = points[1].y = y1 + 1 - w - d;
|
|
points[2].x = x1 - 1 + style->borderWidth.left;
|
|
points[3].x = x2 + 2 - style->borderWidth.right;
|
|
points[2].y = points[3].y = y1 + 1 - style->borderWidth.bottom;
|
|
view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void drawBorderLeft(View *view, Style *style,
|
|
int x1, int y1, int x2, int y2)
|
|
|
|
{
|
|
int d, w;
|
|
Point points[4];
|
|
bool filled = true, convex = true;
|
|
bool ridge = false, inset = false, dotted = false;
|
|
Color::Shading shading = Color::SHADING_NORMAL;
|
|
|
|
if (!style->borderColor.left || style->borderWidth.left == 0)
|
|
return;
|
|
|
|
switch (style->borderStyle.left) {
|
|
case BORDER_NONE:
|
|
case BORDER_HIDDEN:
|
|
break;
|
|
case BORDER_DOTTED:
|
|
dotted = true;
|
|
/* fallthrough */
|
|
case BORDER_DASHED:
|
|
w = style->borderWidth.left;
|
|
view->drawTypedLine(style->borderColor.left, shading,
|
|
dotted ? LINE_DOTTED : LINE_DASHED,
|
|
w, x1+w/2, y1+w/2, x1+w/2, y2-w/2);
|
|
break;
|
|
case BORDER_SOLID:
|
|
case BORDER_INSET:
|
|
inset = true;
|
|
/* fallthrough */
|
|
case BORDER_OUTSET:
|
|
if (style->borderStyle.left != BORDER_SOLID)
|
|
shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
if (style->borderWidth.left == 1) { /* 1 pixel line */
|
|
view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2);
|
|
} else {
|
|
points[0].x = points[1].x = x1;
|
|
points[0].y = y1 - 1;
|
|
points[1].y = y2 + 1;
|
|
points[2].x = points[3].x = points[0].x + style->borderWidth.left;
|
|
points[2].y = points[1].y - style->borderWidth.bottom;
|
|
points[3].y = points[0].y + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.left, shading, filled, convex,
|
|
points, 4);
|
|
}
|
|
break;
|
|
case BORDER_RIDGE:
|
|
ridge = true;
|
|
/* fallthrough */
|
|
case BORDER_GROOVE:
|
|
w = style->borderWidth.left;
|
|
d = w & 1;
|
|
points[0].x = points[1].x = x1;
|
|
points[0].y = y1;
|
|
points[1].y = y2;
|
|
points[2].x = points[3].x = x1 + w / 2 + d;
|
|
points[2].y = y2 - style->borderWidth.bottom / 2;
|
|
points[3].y = y1 + style->borderWidth.top / 2;
|
|
shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
view->drawPolygon (style->borderColor.left, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = points[1].x = x1 + w / 2 + d;
|
|
points[0].y = y1 + style->borderWidth.top / 2;
|
|
points[1].y = y2 - style->borderWidth.bottom / 2;
|
|
points[2].x = points[3].x = x1 + w;
|
|
points[2].y = y2 - style->borderWidth.bottom;
|
|
points[3].y = y1 + style->borderWidth.top;
|
|
shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
view->drawPolygon (style->borderColor.left, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
case BORDER_DOUBLE:
|
|
w = (int) rint(style->borderWidth.left / 3.0);
|
|
d = w ? style->borderWidth.left - 2 * w : 0;
|
|
int w_b = (int) rint(style->borderWidth.bottom / 3.0);
|
|
int w_t = (int) rint(style->borderWidth.top / 3.0);
|
|
if (style->borderWidth.left == 1) {
|
|
view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2-1);
|
|
break;
|
|
}
|
|
points[0].x = points[1].x = x1;
|
|
points[0].y = y1 - 1;
|
|
points[1].y = y2 + 1;
|
|
points[2].x = points[3].x = points[0].x + w;
|
|
points[2].y = points[1].y - w_b;
|
|
points[3].y = points[0].y + w_t;
|
|
view->drawPolygon (style->borderColor.left, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = points[1].x = x1 + w + d;
|
|
points[0].y = y1 - 1 + style->borderWidth.top - w_t;
|
|
points[1].y = y2 + 1 - style->borderWidth.bottom + w_b;
|
|
points[2].x = points[3].x = points[0].x + w;
|
|
points[2].y = y2 + 1 - style->borderWidth.bottom;
|
|
points[3].y = y1 - 1 + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.left, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void drawBorderRight(View *view, Style *style,
|
|
int x1, int y1, int x2, int y2)
|
|
|
|
{
|
|
int d, w;
|
|
Point points[4];
|
|
const bool filled = true, convex = true;
|
|
bool ridge = false, inset = false, dotted = false;
|
|
Color::Shading shading = Color::SHADING_NORMAL;
|
|
|
|
if (!style->borderColor.right || style->borderWidth.right == 0)
|
|
return;
|
|
|
|
switch (style->borderStyle.right) {
|
|
case BORDER_NONE:
|
|
case BORDER_HIDDEN:
|
|
break;
|
|
case BORDER_DOTTED:
|
|
dotted = true;
|
|
/* fallthrough */
|
|
case BORDER_DASHED:
|
|
w = style->borderWidth.right;
|
|
view->drawTypedLine(style->borderColor.right, shading,
|
|
dotted ? LINE_DOTTED : LINE_DASHED,
|
|
w, x1 - w/2, y1 + w/2, x1 - w/2, y2 - w/2);
|
|
break;
|
|
case BORDER_SOLID:
|
|
case BORDER_INSET:
|
|
inset = true;
|
|
/* fallthrough */
|
|
case BORDER_OUTSET:
|
|
if (style->borderStyle.right != BORDER_SOLID)
|
|
shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
if (style->borderWidth.right == 1) { /* 1 pixel line */
|
|
view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2);
|
|
} else {
|
|
points[0].x = points[1].x = x1 + 1;
|
|
points[0].y = y1 - 1;
|
|
points[1].y = y2 + 1;
|
|
points[2].x = points[3].x = points[0].x-style->borderWidth.right;
|
|
points[2].y = points[1].y - style->borderWidth.bottom;
|
|
points[3].y = points[0].y + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.right, shading, filled, convex,
|
|
points,4);
|
|
}
|
|
break;
|
|
case BORDER_RIDGE:
|
|
ridge = true;
|
|
/* fallthrough */
|
|
case BORDER_GROOVE:
|
|
w = style->borderWidth.right;
|
|
d = w & 1;
|
|
points[0].x = points[1].x = x1 + 1;
|
|
points[0].y = y1;
|
|
points[1].y = y2;
|
|
points[2].x = points[3].x = points[0].x - w / 2 - d;
|
|
points[2].y = y2 - style->borderWidth.bottom / 2;
|
|
points[3].y = points[0].y + style->borderWidth.top / 2;
|
|
shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
view->drawPolygon (style->borderColor.right, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = points[1].x = x1 + 1 - w / 2 - d;
|
|
points[0].y = y1 + style->borderWidth.top / 2;
|
|
points[1].y = y2 - style->borderWidth.bottom / 2;
|
|
points[2].x = points[3].x = x1 + 1 - w;
|
|
points[2].y = y2 - style->borderWidth.bottom;
|
|
points[3].y = y1 + style->borderWidth.top;
|
|
shading = (ridge) ? Color::SHADING_LIGHT: Color::SHADING_DARK;
|
|
view->drawPolygon (style->borderColor.right, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
case BORDER_DOUBLE:
|
|
w = (int) rint(style->borderWidth.right / 3.0);
|
|
d = w ? style->borderWidth.right - 2 * w : 0;
|
|
int w_b = (int) rint(style->borderWidth.bottom / 3.0);
|
|
int w_t = (int) rint(style->borderWidth.top / 3.0);
|
|
if (style->borderWidth.right == 1) {
|
|
view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2);
|
|
break;
|
|
}
|
|
points[0].x = points[1].x = x1 + 1;
|
|
points[0].y = y1 - 1;
|
|
points[1].y = y2 + 1;
|
|
points[2].x = points[3].x = points[0].x - w;
|
|
points[2].y = points[1].y - w_b;
|
|
points[3].y = points[0].y + w_t;
|
|
view->drawPolygon (style->borderColor.right, shading, filled, convex,
|
|
points, 4);
|
|
points[0].x = points[1].x = x1 + 1 - w - d;
|
|
points[0].y = y1 - 1 + style->borderWidth.top - w_t;
|
|
points[1].y = y2 + 1 - style->borderWidth.bottom + w_b;
|
|
points[2].x = points[3].x = points[0].x - w;
|
|
points[2].y = y2 + 1 - style->borderWidth.bottom;
|
|
points[3].y = y1 - 1 + style->borderWidth.top;
|
|
view->drawPolygon (style->borderColor.right, shading, filled, convex,
|
|
points, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \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, Layout *layout, Rectangle *area,
|
|
int x, int y, int width, int height,
|
|
Style *style, bool inverse)
|
|
{
|
|
/** \todo a lot! */
|
|
int xb1, yb1, xb2, yb2;
|
|
|
|
// top left and bottom right point of outer border boundary
|
|
xb1 = x + style->margin.left;
|
|
yb1 = y + style->margin.top;
|
|
xb2 = x + (width > 0 ? width - 1 : 0) - style->margin.right;
|
|
yb2 = y + (height > 0 ? height - 1 : 0) - style->margin.bottom;
|
|
|
|
/*
|
|
// top left and bottom right point of inner border boundary
|
|
xp1 = xb1 + style->borderWidth.left;
|
|
yp1 = yb1 + style->borderWidth.top;
|
|
xp2 = xb2 - style->borderWidth.right;
|
|
yp2 = yb2 - style->borderWidth.bottom;
|
|
|
|
light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT;
|
|
dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK;
|
|
normal = inverse ? Color::SHADING_INVERSE : Color::SHADING_NORMAL;
|
|
*/
|
|
|
|
drawBorderRight(view, style, xb2, yb1, xb2, yb2);
|
|
drawBorderLeft(view, style, xb1, yb1, xb1, yb2);
|
|
drawBorderTop(view, style, xb1, yb1, xb2, yb1);
|
|
drawBorderBottom(view, style, xb1, yb2, xb2, yb2);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Draw the background (content plus padding) 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 (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 information; 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 necessary, 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.
|
|
*
|
|
* "bgColor" is passes implicitly. For non-inversed drawing,
|
|
* style->backgroundColor may simply used. However, when drawing is
|
|
* inversed, and style->backgroundColor is undefined (NULL), a
|
|
* background color defined higher in the hierarchy (which is not
|
|
* accessible here) must be used.
|
|
*
|
|
* (Background *images* are never drawn inverse.)
|
|
*/
|
|
void drawBackground (View *view, Layout *layout, Rectangle *area,
|
|
int x, int y, int width, int height,
|
|
int xRef, int yRef, int widthRef, int heightRef,
|
|
Style *style, Color *bgColor, bool inverse, bool atTop)
|
|
{
|
|
bool hasBgColor = bgColor != NULL &&
|
|
// The test for background colors is rather simple, since only the color
|
|
// has to be compared, ...
|
|
(!atTop || layout->getBgColor () != bgColor);
|
|
bool hasBgImage = (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 (hasBgColor || hasBgImage) {
|
|
Rectangle bgArea, intersection;
|
|
bgArea.x = x;
|
|
bgArea.y = y;
|
|
bgArea.width = width;
|
|
bgArea.height = height;
|
|
|
|
if (area->intersectsWith (&bgArea, &intersection)) {
|
|
if (hasBgColor)
|
|
view->drawRectangle (bgColor,
|
|
inverse ?
|
|
Color::SHADING_INVERSE : Color::SHADING_NORMAL,
|
|
true, intersection.x, intersection.y,
|
|
intersection.width, intersection.height);
|
|
|
|
if (hasBgImage)
|
|
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 = std::max (xt, x);
|
|
int x2 = std::min (xt + imgWidthT, x + width);
|
|
int yt = origY + tileY * imgHeightS;
|
|
int y1 = std::max (yt, y);
|
|
int y2 = std::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;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
static const char
|
|
*const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" },
|
|
*const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" },
|
|
*const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" },
|
|
*const roman_I3[] = { "","M","MM","MMM","MMMM" };
|
|
|
|
static void strAsciiTolower (char *s)
|
|
{
|
|
for ( ; *s; s++)
|
|
*s = misc::AsciiTolower (*s);
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a number into a string, in a given list style.
|
|
*
|
|
* Used for ordered lists.
|
|
*/
|
|
void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType)
|
|
{
|
|
int i3, i2, i1, i0;
|
|
bool low = false;
|
|
int start_ch = 'A';
|
|
|
|
if (buflen <= 0)
|
|
return;
|
|
|
|
switch(listStyleType){
|
|
case LIST_STYLE_TYPE_LOWER_ALPHA:
|
|
case LIST_STYLE_TYPE_LOWER_LATIN:
|
|
start_ch = 'a';
|
|
/* fallthrough */
|
|
case LIST_STYLE_TYPE_UPPER_ALPHA:
|
|
case LIST_STYLE_TYPE_UPPER_LATIN:
|
|
i0 = num - 1;
|
|
i1 = i0/26 - 1; i2 = i1/26 - 1;
|
|
if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */
|
|
snprintf(buf, buflen, "****.");
|
|
else
|
|
snprintf(buf, buflen, "%c%c%c.",
|
|
i2<0 ? ' ' : start_ch + i2%26,
|
|
i1<0 ? ' ' : start_ch + i1%26,
|
|
i0<0 ? ' ' : start_ch + i0%26);
|
|
break;
|
|
case LIST_STYLE_TYPE_LOWER_ROMAN:
|
|
low = true;
|
|
/* fallthrough */
|
|
case LIST_STYLE_TYPE_UPPER_ROMAN:
|
|
i0 = num;
|
|
i1 = i0/10; i2 = i1/10; i3 = i2/10;
|
|
i0 %= 10; i1 %= 10; i2 %= 10;
|
|
if (num < 0 || i3 > 4) /* more than 4999 elements ? */
|
|
snprintf(buf, buflen, "****.");
|
|
else
|
|
snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2],
|
|
roman_I1[i1], roman_I0[i0]);
|
|
break;
|
|
case LIST_STYLE_TYPE_DECIMAL:
|
|
default:
|
|
snprintf(buf, buflen, "%d.", num);
|
|
break;
|
|
}
|
|
|
|
// ensure termination
|
|
buf[buflen - 1] = '\0';
|
|
|
|
if (low)
|
|
strAsciiTolower(buf);
|
|
|
|
}
|
|
|
|
} // namespace style
|
|
} // namespace core
|
|
} // namespace dw
|