Commit c5b694d0 authored by Enrico Ros's avatar Enrico Ros
Browse files

Painter_AGG2:

  Part from the *very C00L* AGG2 library (www.antigrain.com) are imported
  from the agg23 source package. The imported files provides antialiased
  rendering on bgra32 qimage memory buffers.
  See "kpdf/ui/painter_agg2/README.kpdf" for more info.
PagePainter:
  Replaced my dear crappy scanline renderer (well, was the fastest btw :-)
  with agg2 based rendering code.
  Implemented HighlightAnnotation (HL, Underline, Strikeout and Squiggly)
  and InkAnnotation (simple one) rendering.
  Need a multiply-blending template algo for getting highlights to look
  as highlighs (not solid or transparent, like now).
Makefile.am(s):
  Updated to build the new library, set include paths and link it.

Here we go.

svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=405150
parent 8bc57116
......@@ -17,8 +17,8 @@ kde_module_LTLIBRARIES = libkpdfpart.la
libkpdfpart_la_SOURCES = dcop.skel error.cpp part.cpp
libkpdfpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
libkpdfpart_la_LIBADD = xpdf/xpdf/libxpdf.la conf/libkpdfconf.la \
core/libkpdfcore.la ui/libkpdfui.la $(LIB_KPARTS) \
libkpdfpart_la_LIBADD = xpdf/xpdf/libxpdf.la conf/libkpdfconf.la core/libkpdfcore.la \
ui/libkpdfui.la ui/painter_agg2/libagg2.la $(LIB_KPARTS) \
$(LIB_KFILE) $(LIB_KDEPRINT) $(LIB_KUTILS) -lm
partdesktopdir = $(kde_servicesdir)
......
SUBDIRS = data
SUBDIRS = data painter_agg2 .
INCLUDES = -I$(srcdir)/.. -I$(top_builddir)/kpdf $(all_includes)
INCLUDES = -I$(srcdir)/.. -I$(srcdir)/painter_agg2 -I$(top_builddir)/kpdf $(all_includes)
METASOURCES = AUTO
......
......@@ -241,46 +241,135 @@ void PagePainter::paintPageOnPainter( QPainter * destPainter, const KPDFPage * p
// 4B.4. paint annotations [COMPOSITED ONES]
if ( bufferedAnnotations )
{
// iterate over annotations and paint AText, ALine, AHighlight, AInk
// precalc costants for normalizing the quads to the image
double xOffset = (float)limits.left() / (float)scaledWidth,
xScale = (float)scaledWidth / (float)limits.width(),
yOffset = (float)limits.top() / (float)scaledHeight,
yScale = (float)scaledHeight / (float)limits.height();
// paint all buffered annotations in the page
QValueList< Annotation * >::const_iterator aIt = bufferedAnnotations->begin(), aEnd = bufferedAnnotations->end();
for ( ; aIt != aEnd; ++aIt )
{
Annotation * a = *aIt;
Annotation::SubType type = a->subType();
// draw HighlightAnnotation
if ( type == Annotation::AHighlight )
// draw TextAnnotation (InPlace) MISSING: all
if ( type == Annotation::AText )
{
// TODO
}
// draw LineAnnotation MISSING: all
else if ( type == Annotation::ALine )
{
// TODO
// get the annotation
/*LineAnnotation * la = (LineAnnotation *) a;
NormalizedPath path;
// normalize page point to image
const NormalizedPoint & inkPoint = *pIt;
NormalizedPoint point;
point.x = (inkPoint.x - xOffset) * xScale;
point.y = (inkPoint.y - yOffset) * yScale;
path.append( point );
// draw the normalized path into image
drawShapeOnImage( backImage, path, false, QPen( a->style.color ), QBrush(), Blend );
*/
}
// draw GeomAnnotation MISSING: all
else if ( type == Annotation::AGeom )
{
// TODO
}
// draw HighlightAnnotation MISSING: under/strike width, feather, capping
else if ( type == Annotation::AHighlight )
{
// get the annotation
HighlightAnnotation * ha = (HighlightAnnotation *) a;
// precalc costants for normalizing the quads to the image
int quads = ha->highlightQuads.size();
double xOffset = (float)limits.left() / (float)scaledWidth,
xScale = (float)scaledWidth / (float)limits.width(),
yOffset = (float)limits.top() / (float)scaledHeight,
yScale = (float)scaledHeight / (float)limits.height();
HighlightAnnotation::HighlightType type = ha->highlightType;
// draw each quad of the annotation
int quads = ha->highlightQuads.size();
for ( int q = 0; q < quads; q++ )
{
NormalizedPath path;
const HighlightAnnotation::Quad & quad = ha->highlightQuads[ q ];
// normalize page point to image
for ( int i = 0; i < 4; i++ )
{
// normalize page point to image
NormalizedPoint point;
point.x = (quad.points[ i ].x - xOffset) * xScale;
point.y = (quad.points[ i ].y - yOffset) * yScale;
path.append( point );
}
//drawShapeOnImage( backImage, path );
// draw the normalized path into image
switch ( type )
{
// highlight the whole rect
case HighlightAnnotation::Highlight:
drawShapeOnImage( backImage, path, true, QPen(), a->style.color, Multiply );
break;
// highlight the bottom part of the rect
case HighlightAnnotation::Squiggly:
path[ 0 ].x = ( path[ 0 ].x + path[ 3 ].x ) / 2.0;
path[ 0 ].y = ( path[ 0 ].y + path[ 3 ].y ) / 2.0;
path[ 1 ].x = ( path[ 1 ].x + path[ 2 ].x ) / 2.0;
path[ 1 ].y = ( path[ 1 ].y + path[ 2 ].y ) / 2.0;
drawShapeOnImage( backImage, path, true, QPen(), a->style.color, Multiply );
break;
// make a line at 3/4 of the height
case HighlightAnnotation::Underline:
path[ 0 ].x = ( path[ 0 ].x + 3*path[ 3 ].x ) / 4.0;
path[ 0 ].y = ( path[ 0 ].y + 3*path[ 3 ].y ) / 4.0;
path[ 1 ].x = ( path[ 1 ].x + 3*path[ 2 ].x ) / 4.0;
path[ 1 ].y = ( path[ 1 ].y + 3*path[ 2 ].y ) / 4.0;
path.pop_back();
path.pop_back();
drawShapeOnImage( backImage, path, false, QPen( a->style.color, 2 ), QBrush(), Blend );
break;
// make a line at 1/2 of the height
case HighlightAnnotation::StrikeOut:
path[ 0 ].x = ( path[ 0 ].x + path[ 3 ].x ) / 2.0;
path[ 0 ].y = ( path[ 0 ].y + path[ 3 ].y ) / 2.0;
path[ 1 ].x = ( path[ 1 ].x + path[ 2 ].x ) / 2.0;
path[ 1 ].y = ( path[ 1 ].y + path[ 2 ].y ) / 2.0;
path.pop_back();
path.pop_back();
drawShapeOnImage( backImage, path, false, QPen( a->style.color, 2 ), QBrush(), Blend );
break;
}
}
}
// draw InkAnnotation
//else if ( type == Annotation::AInk )
}
}
// draw InkAnnotation MISSING:invar width, PENTRACER
else if ( type == Annotation::AInk )
{
// get the annotation
InkAnnotation * ia = (InkAnnotation *) a;
// draw each ink path
int paths = ia->inkPaths.size();
for ( int p = 0; p < paths; p++ )
{
NormalizedPath path;
const QValueList<NormalizedPoint> & inkPath = ia->inkPaths[ p ];
// normalize page point to image
QValueList<NormalizedPoint>::const_iterator pIt = inkPath.begin(), pEnd = inkPath.end();
for ( ; pIt != pEnd; ++pIt )
{
const NormalizedPoint & inkPoint = *pIt;
NormalizedPoint point;
point.x = (inkPoint.x - xOffset) * xScale;
point.y = (inkPoint.y - yOffset) * yScale;
path.append( point );
}
// draw the normalized path into image
drawShapeOnImage( backImage, path, false, QPen( a->style.color, 2 ), QBrush(), Blend );
}
}
} // end current annotation drawing
}
// 4B.5. create the back pixmap converting from the local image
backPixmap = new QPixmap( backImage );
......@@ -350,7 +439,7 @@ void PagePainter::paintPageOnPainter( QPainter * destPainter, const KPDFPage * p
mixedPainter->drawPixmap( annotRect.topLeft(), pixmap );
}
// draw GeomAnnotation
else
else // WARNING: TEMPORARY CODE! migrate everything to AGG
{
//GeomAnnotation * geom = (GeomAnnotation *)a;
//if ( geom->geomType == GeomAnnotation::InscribedSquare )
......@@ -534,135 +623,93 @@ void PagePainter::colorizeImage( QImage & grayImage, const QColor & color,
}
}
#define dbl_swap(x, y) { double tmp = (x); (x) = (y); (y) = tmp; }
//BEGIN of Anti-Grain dependant code
/** Shape Drawing using Anti-Grain Geometry library **/
// The following code uses the AGG2.3 lib imported into the "painter_agg2"
// directory. This is to be replaced by Arthur calls for drawing antialiased
// primitives, but until that AGG2 does its job very fast and good-looking.
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_renderer_base.h"
#include "agg_scanline_u.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_conv_stroke.h"
#include "agg_path_storage.h"
void PagePainter::drawShapeOnImage(
QImage & image,
const NormalizedPath & path,
const NormalizedPath & normPath,
bool closeShape,
const QPen & pen,
const QBrush & brush,
float /*antiAliasRadius*/ )
DrawingOperation op
//float antiAliasRadius
)
{
// safety checks
int points = path.size();
if ( points < 2 )
int pointsNumber = normPath.size();
if ( pointsNumber < 2 )
return;
// transform NormalizedPoints inside image
struct TPoint {
double x;
double y;
};
TPoint point[ points + 1 ];
for ( int p = 0; p < points; p++ )
int imageWidth = image.width();
int imageHeight = image.height();
double fImageWidth = (double)imageWidth;
double fImageHeight = (double)imageHeight;
// create a 'path'
agg::path_storage path;
path.move_to( normPath[ 0 ].x * fImageWidth, normPath[ 0 ].y * fImageHeight );
for ( int i = 1; i < pointsNumber; i++ )
path.line_to( normPath[ i ].x * fImageWidth, normPath[ i ].y * fImageHeight );
if ( closeShape )
path.close_polygon();
// create the 'rendering buffer' over qimage memory
agg::rendering_buffer buffer( image.bits(), imageWidth, imageHeight, imageWidth << 2 );
// create 'pixel buffer', 'clipped renderer', 'scanline renderer' on bgra32 format
typedef agg::pixfmt_bgra32 bgra32;
typedef agg::renderer_base< bgra32 > rb_bgra32;
bgra32 * pixels = new bgra32( buffer );
rb_bgra32 rb( *pixels );
agg::renderer_scanline_aa_solid< rb_bgra32 > render( rb );
// create rasterizer and scaline
agg::rasterizer_scanline_aa<> rasterizer;
agg::scanline_u8 scanline;
#if 0
//draw RAINBOW
agg::rgba8 span[ imageWidth ];
for( int x = 0; x < imageWidth; x++ )
{
point[ p ].x = path[ p ].x * (double)image.width();
point[ p ].y = path[ p ].y * (double)image.height();
agg::rgba c( 380.0 + 400.0 * x / imageWidth, 0.8 );
span[ x ] = agg::rgba8(c);
}
point[ points ].x = point[ 0 ].x;
point[ points ].y = point[ 0 ].y;
for( int y = 0; y < imageHeight; y++ )
pixels->blend_color_hspan( 0, y, imageWidth, span, 0, (255*y)/imageHeight );
#endif
// create and clear scanline buffers
int imageHeight = image.height();
int imageWidth = image.width();
struct ScanLine
{
bool haveStart;
double start;
bool haveStop;
double stop;
ScanLine() : haveStart( false ), haveStop( false ) {};
};
ScanLine lines[ imageHeight ];
// compute scanlines buffers
for ( int l = 0; l < points; l++ )
// fill rect
if ( brush.style() != Qt::NoBrush )
{
bool downward = point[ l ].y < point[ l + 1 ].y;
const TPoint & p1 = downward ? point[ l ] : point[ l + 1 ];
const TPoint & p2 = downward ? point[ l + 1 ] : point[ l ];
// scan vertically from p1 to p2
int startY = (int)round( p1.y );
int endY = (int)round( p2.y );
// special horizontal case
int deltaY = endY - startY;
if ( !deltaY )
{
if ( startY >= 0 && startY < imageHeight )
{
ScanLine & line = lines[ startY ];
line.start = p1.x;
line.haveStart = true;
line.stop = p2.x;
line.haveStop = true;
}
continue;
}
// standard case
double dX = p2.x - p1.x;
for ( int y = startY; y < endY; y ++ )
{
// only process image area
if ( y < 0 || y >= imageHeight )
continue;
double x = p1.x + ((double)(y-startY) / (double)deltaY) * dX;
ScanLine & line = lines[ y ];
if ( !line.haveStart )
{
// add start
line.start = x;
line.haveStart = true;
}
else if ( !line.haveStop )
{
// add stop
line.stop = x;
line.haveStop = true;
if ( line.start > line.stop )
dbl_swap( line.start, line.stop );
}
else
{
// refine bounds
if ( x < line.start )
line.start = x;
else if ( x > line.stop )
line.stop = x;
}
}
const QColor & brushColor = brush.color();
render.color( agg::rgba8( brushColor.red(), brushColor.green(), brushColor.blue() ) );
rasterizer.add_path( path );
agg::render_scanlines( rasterizer, scanline, render );
rasterizer.reset();
}
// fill scanlines NOTE: bottom scanline is not visible? PLEASE CHECK!
unsigned int * data = (unsigned int *)image.bits();
int src, newR, newG, newB,
rh = brush.color().red(),
gh = brush.color().green(),
bh = brush.color().blue();
for ( int i = 0; i < imageHeight; i++ )
// stroke outline
if ( pen.width() != 0 )
{
// get the current line and check it
ScanLine & line = lines[ i ];
if ( !line.haveStart || !line.haveStop ||
line.start > imageWidth || line.stop < 0 )
continue;
if ( line.start > line.stop )
dbl_swap( line.start, line.stop );
// fill pixels
int lineStart = line.start > 0 ? (int)line.start : 0;
int lineStop = line.stop < imageWidth ? (int)line.stop : imageWidth - 1;
int dataOffset = i * imageWidth + lineStart;
for ( int x = lineStart; x <= lineStop; x++ )
{
src = data[ dataOffset ];
newR = qt_div_255( qRed(src) * rh );
newG = qt_div_255( qGreen(src) * gh );
newB = qt_div_255( qBlue(src) * bh );
data[ dataOffset ] = qRgba( newR, newG, newB, qAlpha(src) );
dataOffset++;
}
const QColor & penColor = pen.color();
render.color( agg::rgba8( penColor.red(), penColor.green(), penColor.blue() ) );
agg::conv_stroke< agg::path_storage > strokedPath( path );
strokedPath.width( pen.width() );
rasterizer.add_path( strokedPath );
agg::render_scanlines( rasterizer, scanline, render );
}
}
//END of Anti-Grain dependant code
......@@ -53,12 +53,15 @@ class PagePainter
// my pretty dear raster function
typedef QValueList< NormalizedPoint > NormalizedPath;
enum DrawingOperation { Blend, Multiply };
static void drawShapeOnImage(
QImage & image,
const NormalizedPath & imagePoints,
const QPen & pen = Qt::yellow,
const QBrush & brush = Qt::red,
float antiAliasRadius = 1.0
bool closeShape = true,
const QPen & pen = QPen(),
const QBrush & brush = QBrush(),
DrawingOperation op = Blend
//float antiAliasRadius = 1.0
);
};
......
#INCLUDES = -I$(srcdir)/.. -I$(top_builddir)/kpdf $(all_includes)
#SUBDIRS = ctrl . platform
INCLUDES = -I.
#-I$(top_srcdir)/include
noinst_LTLIBRARIES = libagg2.la
libagg2_la_SOURCES = agg_path_storage.cpp agg_vcgen_stroke.cpp agg_rasterizer_scanline_aa.cpp agg_bezier_arc.cpp \
agg_trans_affine.cpp
# agg_arc.cpp agg_arrowhead.cpp agg_bezier_arc.cpp agg_bspline.cpp agg_curves.cpp \
# agg_vcgen_bspline.cpp agg_vcgen_contour.cpp agg_vcgen_dash.cpp agg_vcgen_markers_term.cpp \
# agg_vcgen_smooth_poly1.cpp agg_vcgen_stroke.cpp agg_gsv_text.cpp agg_image_filters.cpp \
# agg_path_storage.cpp agg_rasterizer_scanline_aa.cpp agg_line_aa_basics.cpp agg_line_profile_aa.cpp \
# agg_rounded_rect.cpp agg_sqrt_tables.cpp agg_embedded_raster_fonts.cpp agg_trans_affine.cpp \
# agg_trans_single_path.cpp agg_trans_double_path.cpp \
# agg_trans_warp_magnifier.cpp agg_vpgen_clip_polygon.cpp agg_vpgen_clip_polyline.cpp \
# agg_vpgen_segmentator.cpp
#libagg_la_LIBADD = $(top_builddir)/gpc/libagggpc.la $(top_builddir)/src/ctrl/libaggctrl.la
File: kpdf/ui/painter_agg2/README.kpdf
Created by: Enrico Ros @ KPDF project 2005
>> Anti-Grain Geometry 2.3
The files contained in this directory are from the Anti-Grain Geometry library,
that provides many loosely copuled algorithms and classes for performing fine
rendering. All files in there are using a good bsd-like licence compatible with
kpdf development.
No external dependancies required other than a standard C++ compiler suite.
>> File Naming
- "agg_" prefixed files are from the Anti-Grain Geometry distribution. Those
files are updated to the 2.3 release of that library
- "kpdf_" prefixed files are made by kpdf developers to extend library
capabilities and fit the needs of that project.
- README.kpdf must be updated to reflect changes happening in that directory.
>> Directory contents
As AGG is a template based collection of tools, there is no need for the full
library, we need only a small subset of it. In fact in that directory there
are mixed Headers (.h) and Implementations (.cpp) files taken respectively
from:
.h: agg23/include
.cpp: agg23/src
Note that directories "agg23/src/platform" (crossplatform GUI support),
"agg23/src/ctrl" (GUI widgets), "agg23/gpc" (generic polygon clipper),
"agg23/svg" (SVG viewer tool) are NOT USED AT ALL in the kpdf project.
>> File inclusion by scope
< Memory Buffer, RGBA color buffer, ScanLine, ScanLine Renderer,
Path Storage, Path Stroker, Rendering >
agg_array.h, agg_basics.h, agg_bezier_arc.h, agg_clip_liang_barsky.h,
agg_color_rgba.h, agg_conv_adaptor_vcgen.h, agg_conv_stroke.h,
agg_conv_transform.h, agg_gamma_functions.h, agg_math.h, agg_math_stroke.h,
agg_path_storage.h, agg_pixfmt_rgba.h, agg_rasterizer_scanline_aa.h,
agg_render_scanlines.h, agg_renderer_base.h, agg_renderer_scanline.h,
agg_rendering_buffer.h, agg_scanline_u.h, agg_shorten_path.h,
agg_trans_affine.h, agg_vcgen_stroke.h, agg_vertex_iterator.h,
agg_vertex_sequence.h
agg_bezier_arc.cpp, agg_path_storage.cpp, agg_rasterizer_scanline_aa.cpp,
agg_trans_affine.cpp, agg_vcgen_stroke.cpp
< ..for every new algo added.. >
..append added files here..
>> For Maxim Shemanarev
Thanks for your work - the kpdf team
This diff is collapsed.
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_BASICS_INCLUDED
#define AGG_BASICS_INCLUDED
#if defined(_MSC_VER)
#pragma warning(disable:4786) // Identifier was truncated...
#endif
#if defined(_MSC_VER)
#define AGG_INLINE __forceinline
#else
#define AGG_INLINE inline
#endif
namespace agg
{
//-------------------------------------------------------------------------
typedef signed char int8; //----int8
typedef unsigned char int8u; //----int8u
typedef signed short int16; //----int16
typedef unsigned short int16u; //----int16u
typedef signed int int32; //----int32
typedef unsigned int int32u; //----int32u
#if defined(_MSC_VER)
typedef signed __int64 int64; //---- int64
typedef unsigned __int64 int64u; //---- int64u
#else
typedef signed long long int64; // not sure about the compatibility
typedef unsigned long long int64u;
#endif
//-------------------------------------------------------------------------
typedef unsigned char cover_type; //----cover_type
enum
{
cover_shift = 8, //----cover_shift
cover_size = 1 << cover_shift, //----cover_size
cover_mask = cover_size - 1, //----cover_mask
cover_none = 0, //----cover_none
cover_full = cover_mask //----cover_full
};
//-----------------------------------------------------------------------pi
const double pi = 3.14159265358979323846;
//------------------------------------------------------------------deg2rad
inline double deg2rad(double deg)
{
return deg * pi / 180.0;
}
//------------------------------------------------------------------rad2deg
inline double rad2deg(double rad)
{
return rad * 180.0 / pi;
}
//----------------------------------------------------------------rect_base
template<class T> struct rect_base
{
typedef rect_base<T> self_type;
T x1;
T y1;
T x2;
T y2;
rect_base() {}
rect_base(T x1_, T y1_, T x2_, T y2_) :
x1(x1_), y1(y1_), x2(x2_), y2(y2_) {}
const self_type& normalize()
{
T t;
if(x1 > x2) { t = x1; x1 = x2; x2 = t; }
if(y1 > y2) { t = y1; y1 = y2; y2 = t; }
return *this;
}
bool clip(const self_type& r)
{
if(x2 > r.x2) x2 = r.x2;
if(y2 > r.y2) y2 = r.y2;
if(x1 < r.x1) x1 = r.x1;
if(y1 < r.y1) y1 = r.y1;
return x1 <= x2 && y1 <= y2;
}
bool is_valid() const
{
return x1 <= x2 && y1 <= y2;
}
};
//-----------------------------------------------------intersect_rectangles
template<class Rect>
inline Rect intersect_rectangles(const Rect& r1, const Rect& r2)
{
Rect r = r1;
// First process x2,y2 because the other order
// results in Internal Compiler Error under
// Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in
// case of "Maximize Speed" optimization option.
//-----------------
if(r.x2 > r2.x2) r.x2 = r2.x2;