📄 render_text.cpp
字号:
/** * This file is part of the DOM implementation for KDE. * * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) * (C) 2000-2003 Dirk Mueller (mueller@kde.org) * (C) 2003 Apple Computer, Inc. * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * *///#define DEBUG_LAYOUT//#define BIDI_DEBUG#ifdef HAVE_CONFIG_H#include <config.h>#endif#include "rendering/render_text.h"#include "rendering/render_canvas.h"#include "rendering/break_lines.h"#include "rendering/render_arena.h"#include "xml/dom_nodeimpl.h"#include "misc/loader.h"#include "misc/helper.h"#include <qbitmap.h>#include <qimage.h>#include <qpainter.h>#include <kdebug.h>#include <kglobal.h>#include <assert.h>#include <limits.h>#include <math.h>#ifdef HAVE_ALLOCA_H// explicitly included for systems that don't provide it in stdlib.h#include <alloca.h>#else#include <stdlib.h>#endifusing namespace khtml;using namespace DOM;#ifndef NDEBUGstatic bool inInlineTextBoxDetach;#endifvoid InlineTextBox::detach(RenderArena* renderArena){ if (m_parent) m_parent->removeFromLine(this);#ifndef NDEBUG inInlineTextBoxDetach = true;#endif delete this;#ifndef NDEBUG inInlineTextBoxDetach = false;#endif // Recover the size left there for us by operator delete and free the memory. renderArena->free(*(size_t *)this, this);}void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw(){ return renderArena->allocate(sz);}void InlineTextBox::operator delete(void* ptr, size_t sz){ assert(inInlineTextBoxDetach); // Stash size where detach can find it. *(size_t *)ptr = sz;}/** returns the proper ::selection pseudo style for the given element * @return the style or 0 if no ::selection pseudo applies. */inline const RenderStyle *retrieveSelectionPseudoStyle(const RenderObject *obj){ // http://www.w3.org/Style/CSS/Test/CSS3/Selectors/20021129/html/tests/css3-modsel-162.html // is of the opinion that ::selection of parent elements is also to be applied // to children, so let's do it. while (obj) { const RenderStyle *style = obj->style()->getPseudoStyle(RenderStyle::SELECTION); if (style) return style; obj = obj->parent(); }/*wend*/ return 0;}void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos, int deco){ if(startPos > m_len) return; if(startPos < 0) startPos = 0; QColor hc; QColor hbg; const RenderStyle* pseudoStyle = retrieveSelectionPseudoStyle(text); if (pseudoStyle) { // ### support outline (mandated by CSS3) // ### support background-image? (optional by CSS3) if (pseudoStyle->backgroundColor().isValid()) hbg = pseudoStyle->backgroundColor(); hc = pseudoStyle->color(); } else { const QColorGroup &grp = style->palette().active(); hc = grp.highlightedText(); hbg = grp.highlight(); // ### should be at most retrieved once per render text QColor bg = khtml::retrieveBackgroundColor(text); // It may happen that the contrast is -- well -- virtually non existent. // In this case, simply invert the colors if (!khtml::hasSufficientContrast(hbg, bg)) { hc = QColor(0xff-hc.red(),0xff-hc.green(),0xff-hc.blue()); hbg = QColor(0xff-hbg.red(),0xff-hbg.green(),0xff-hbg.blue()); }/*end if*/ } p->setPen(hc); //kdDebug( 6040 ) << "textRun::painting(" << QConstString(text->str->s + m_start, m_len).string().left(30) << ") at(" << m_x+tx << "/" << m_y+ty << ")" << endl; f->drawText(p, m_x + tx, m_y + ty + m_baseline, text->str->s, text->str->l, m_start, m_len, m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, startPos, endPos, hbg, m_y + ty, height(), deco);}void InlineTextBox::paintDecoration( QPainter *pt, const Font *f, int _tx, int _ty, int deco){ _tx += m_x; _ty += m_y; int width = m_width - 1; RenderObject *p = object(); QColor underline, overline, linethrough; p->getTextDecorationColors(deco, underline, overline, linethrough, p->style()->htmlHacks()); if(deco & UNDERLINE){ pt->setPen(underline); f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::UNDERLINE); } if (deco & OVERLINE) { pt->setPen(overline); f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::OVERLINE); } if(deco & LINE_THROUGH) { pt->setPen(linethrough); f->drawDecoration(pt, _tx, _ty, baseline(), width, height(), Font::LINE_THROUGH); } // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to // support it. Lars}void InlineTextBox::paintShadow(QPainter *pt, const Font *f, int _tx, int _ty, const ShadowData *shadow ){ int x = m_x + _tx + shadow->x; int y = m_y + _ty + shadow->y; const RenderText* text = renderText(); if (shadow->blur <= 0) { QColor c = pt->pen().color(); pt->setPen(shadow->color); f->drawText(pt, x, y+m_baseline, text->str->s, text->str->l, m_start, m_len, m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR); pt->setPen(c); } else { const int thickness = shadow->blur; const int w = m_width+2*thickness; const int h = m_height+2*thickness; const QRgb color = shadow->color.rgb(); const int gray = qGray(color); const bool inverse = (gray < 100); const QRgb bgColor = (inverse) ? qRgb(255,255,255) : qRgb(0,0,0); QPixmap pixmap(w, h); pixmap.fill(bgColor); QPainter p; p.begin(&pixmap); p.setPen(shadow->color); p.setFont(pt->font()); f->drawText(&p, thickness, thickness+m_baseline, text->str->s, text->str->l, m_start, m_len, m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR); p.end(); QImage img = pixmap.convertToImage().convertDepth(32); int md = thickness*thickness; // max-dist^2 // blur map (division cache) float *bmap = (float*)alloca(sizeof(float)*(md+1)); for(int n=0; n<=md; n++) { float f; f = n/(float)(md+1); f = 1.0 - f*f; bmap[n] = f; } float factor = 0.0; // maximal opacity-sum for(int n=-thickness; n<=thickness; n++) for(int m=-thickness; m<=thickness; m++) { int d = n*n+m*m; if (d<=md) factor += bmap[d]; } factor = 1.0/factor; // alpha map float* amap = (float*)alloca(sizeof(float)*(h*w)); memset(amap, 0, h*w*(sizeof(float))); for(int j=thickness; j<h-thickness; j++) { for(int i=thickness; i<w-thickness; i++) { QRgb col= img.pixel(i,j); if (col == bgColor) continue; float g = qGray(col); if (inverse) g = (255-g)/(255-gray); else g = g/gray; for(int n=-thickness; n<=thickness; n++) { for(int m=-thickness; m<=thickness; m++) { int d = n*n+m*m; if (d>md) continue; float f = bmap[d]; amap[(i+m)+(j+n)*w] += (g*f); } } } } QImage res(w,h,32); res.setAlphaBuffer(true); int r = qRed(color); int g = qGreen(color); int b = qBlue(color); for(int j=0; j<h; j++) { for(int i=0; i<w; i++) { res.setPixel(i,j, qRgba(r,g,b,(int)(amap[i+j*w]*factor*255.0))); } } pt->drawImage(x-thickness, y-thickness, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither); } // Paint next shadow effect if (shadow->next) paintShadow(pt, f, _tx, _ty, shadow->next);}/** * Distributes pixels to justify text. * @param numSpaces spaces left, will be decremented by one * @param toAdd number of pixels left to be distributed, will have the * amount of pixels distributed during this call subtracted. * @return number of pixels to distribute */static inline int justifyWidth(int &numSpaces, int &toAdd) { int a = 0; if ( numSpaces ) { a = toAdd/numSpaces; toAdd -= a; numSpaces--; }/*end if*/ return a;}FindSelectionResult InlineTextBox::checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineHeight){// kdDebug(6040) << "InlineTextBox::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y// << " _tx+m_x=" << _tx+m_x << " _ty+m_y=" << _ty+m_y << endl; offset = 0; if ( _y < _ty + m_y ) return SelectionPointBefore; // above -> before if ( _y > _ty + m_y + lineHeight ) { // below -> after // Set the offset to the max offset = m_len; return SelectionPointAfter; } if ( _x > _tx + m_x + m_width ) { // to the right return m_reversed ? SelectionPointBeforeInLine : SelectionPointAfterInLine; } // The Y matches, check if we're on the left if ( _x < _tx + m_x ) { return m_reversed ? SelectionPointAfterInLine : SelectionPointBeforeInLine; } // consider spacing for justified text int toAdd = m_toAdd; bool justified = text->style()->textAlign() == JUSTIFY && toAdd > 0; int numSpaces = 0; if (justified) { for( int i = 0; i < m_len; i++ ) if ( text->str->s[m_start+i].category() == QChar::Separator_Space ) numSpaces++; }/*end if*/ int delta = _x - (_tx + m_x); //kdDebug(6040) << "InlineTextBox::checkSelectionPoint delta=" << delta << endl; int pos = 0; if ( m_reversed ) { delta -= m_width; while(pos < m_len) { int w = f->width( text->str->s, text->str->l, m_start + pos); if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space) w += justifyWidth(numSpaces, toAdd); int w2 = w/2; w -= w2; delta += w2; if(delta >= 0) break; pos++; delta += w; } } else { while(pos < m_len) { int w = f->width( text->str->s, text->str->l, m_start + pos); if (justified && text->str->s[m_start + pos].category() == QChar::Separator_Space) w += justifyWidth(numSpaces, toAdd); int w2 = w/2; w -= w2; delta -= w2; if(delta <= 0) break; pos++; delta -= w; } }// kdDebug( 6040 ) << " Text --> inside at position " << pos << endl; offset = pos; return SelectionPointInside;}int InlineTextBox::offsetForPoint(int _x, int &ax) const{ // Do binary search for finding out offset, saves some time for long // runs. int start = 0; int end = m_len; ax = m_x; int offset = (start + end) / 2; while (end - start > 0) { // always snap to the right column. This makes up for "jumpy" vertical // navigation. if (end - start == 1) start = end; offset = (start + end) / 2; ax = m_x + widthFromStart(offset);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -