/*-
* Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* --------------------------------------------------------------------
* Parabolic spline procedures taken from TclTk's tkTrig.c
*
* Copyright (c) 1992-1994 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
*
* See the file "license.terms" in TclTk distribution
* for information on usage and redistribution
* of this code, and for a DISCLAIMER OF ALL WARRANTIES.
* ---------------------------------------------------------------------
*
* $Id$
*/
#include "apricot.h"
#include "Drawable.h"
#include "Image.h"
#include <Drawable.inc>
#ifdef __cplusplus
extern "C" {
#endif
#undef my
#define inherited CComponent->
#define my ((( PDrawable) self)-> self)
#define var (( PDrawable) self)
#define gpARGS Bool inPaint = opt_InPaint
#define gpENTER(fail) if ( !inPaint) if ( !my-> begin_paint_info( self)) return (fail)
#define gpLEAVE if ( !inPaint) my-> end_paint_info( self)
void
Drawable_init( Handle self, HV * profile)
{
dPROFILE;
inherited init( self, profile);
apc_gp_init( self);
var-> w = var-> h = 0;
my-> set_color ( self, pget_i ( color));
my-> set_backColor ( self, pget_i ( backColor));
my-> set_fillWinding ( self, pget_B ( fillWinding));
my-> set_fillPattern ( self, pget_sv( fillPattern));
my-> set_lineEnd ( self, pget_i ( lineEnd));
my-> set_lineJoin ( self, pget_i ( lineJoin));
my-> set_linePattern ( self, pget_sv( linePattern));
my-> set_lineWidth ( self, pget_i ( lineWidth));
my-> set_region ( self, pget_H ( region));
my-> set_rop ( self, pget_i ( rop));
my-> set_rop2 ( self, pget_i ( rop2));
my-> set_textOpaque ( self, pget_B ( textOpaque));
my-> set_textOutBaseline( self, pget_B ( textOutBaseline));
my-> set_splinePrecision( self, pget_i ( splinePrecision));
if ( pexist( translate))
{
AV * av = ( AV *) SvRV( pget_sv( translate));
Point tr = {0,0};
SV ** holder = av_fetch( av, 0, 0);
if ( holder) tr.x = SvIV( *holder); else warn("Array panic on 'translate'");
holder = av_fetch( av, 1, 0);
if ( holder) tr.y = SvIV( *holder); else warn("Array panic on 'translate'");
my-> set_translate( self, tr);
}
SvHV_Font( pget_sv( font), &Font_buffer, "Drawable::init");
my-> set_font( self, Font_buffer);
my-> set_palette( self, pget_sv( palette));
CORE_INIT_TRANSIENT(Drawable);
}
static void
clear_font_abc_caches( Handle self)
{
PList u;
if (( u = var-> font_abc_unicode)) {
int i;
for ( i = 0; i < u-> count; i += 2)
free(( void*) u-> items[ i + 1]);
plist_destroy( u);
var-> font_abc_unicode = nil;
}
if ( var-> font_abc_ascii) {
free( var-> font_abc_ascii);
var-> font_abc_ascii = nil;
}
}
void
Drawable_done( Handle self)
{
clear_font_abc_caches( self);
apc_gp_done( self);
inherited done( self);
}
void
Drawable_cleanup( Handle self)
{
if ( is_opt( optInDrawInfo))
my-> end_paint_info( self);
if ( is_opt( optInDraw))
my-> end_paint( self);
inherited cleanup( self);
}
Bool
Drawable_begin_paint( Handle self)
{
if ( var-> stage > csFrozen) return false;
if ( is_opt( optInDrawInfo)) my-> end_paint_info( self);
opt_set( optInDraw);
var-> splinePrecision_saved = var-> splinePrecision;
return true;
}
void
Drawable_end_paint( Handle self)
{
clear_font_abc_caches( self);
opt_clear( optInDraw);
var-> splinePrecision = var-> splinePrecision_saved;
}
Bool
Drawable_begin_paint_info( Handle self)
{
if ( var-> stage > csFrozen) return false;
if ( is_opt( optInDraw)) return true;
if ( is_opt( optInDrawInfo)) return false;
opt_set( optInDrawInfo);
var-> splinePrecision_saved = var-> splinePrecision;
return true;
}
void
Drawable_end_paint_info( Handle self)
{
clear_font_abc_caches( self);
opt_clear( optInDrawInfo);
var-> splinePrecision = var-> splinePrecision_saved;
}
void
Drawable_set( Handle self, HV * profile)
{
dPROFILE;
if ( pexist( font))
{
SvHV_Font( pget_sv( font), &Font_buffer, "Drawable::set");
my-> set_font( self, Font_buffer);
pdelete( font);
}
if ( pexist( translate))
{
AV * av = ( AV *) SvRV( pget_sv( translate));
Point tr = {0,0};
SV ** holder = av_fetch( av, 0, 0);
if ( holder) tr.x = SvIV( *holder); else warn("Array panic on 'translate'");
holder = av_fetch( av, 1, 0);
if ( holder) tr.y = SvIV( *holder); else warn("Array panic on 'translate'");
my-> set_translate( self, tr);
pdelete( translate);
}
if ( pexist( width) && pexist( height)) {
Point size;
size. x = pget_i( width);
size. y = pget_i( height);
my-> set_size( self, size);
pdelete( width);
pdelete( height);
}
inherited set( self, profile);
}
Font *
Drawable_font_match( char * dummy, Font * source, Font * dest, Bool pick)
{
if ( pick)
apc_font_pick( nilHandle, source, dest);
else
Drawable_font_add( nilHandle, source, dest);
return dest;
}
Bool
Drawable_font_add( Handle self, Font * source, Font * dest)
{
Bool useHeight = source-> height != C_NUMERIC_UNDEF;
Bool useWidth = source-> width != C_NUMERIC_UNDEF;
Bool useSize = source-> size != C_NUMERIC_UNDEF;
Bool usePitch = source-> pitch != C_NUMERIC_UNDEF;
Bool useStyle = source-> style != C_NUMERIC_UNDEF;
Bool useDir = source-> direction != C_NUMERIC_UNDEF;
Bool useName = strcmp( source-> name, C_STRING_UNDEF) != 0;
Bool useEnc = strcmp( source-> encoding, C_STRING_UNDEF) != 0;
/* assignning values */
if ( dest != source) {
if ( useHeight) dest-> height = source-> height;
if ( useWidth ) dest-> width = source-> width;
if ( useDir ) dest-> direction = source-> direction;
if ( useStyle ) dest-> style = source-> style;
if ( usePitch ) dest-> pitch = source-> pitch;
if ( useSize ) dest-> size = source-> size;
if ( useName ) strcpy( dest-> name, source-> name);
if ( useEnc ) strcpy( dest-> encoding, source-> encoding);
}
/* nulling dependencies */
if ( !useHeight && useSize)
dest-> height = 0;
if ( !useWidth && ( usePitch || useHeight || useName || useSize || useDir || useStyle))
dest-> width = 0;
if ( !usePitch && ( useStyle || useName || useDir || useWidth))
dest-> pitch = fpDefault;
if ( useHeight)
dest-> size = 0;
if ( !useHeight && !useSize && ( dest-> height <= 0 || dest-> height > 16383))
useSize = 1;
/* validating entries */
if ( dest-> height <= 0) dest-> height = 1;
else if ( dest-> height > 16383 ) dest-> height = 16383;
if ( dest-> width < 0) dest-> width = 1;
else if ( dest-> width > 16383 ) dest-> width = 16383;
if ( dest-> size <= 0) dest-> size = 1;
else if ( dest-> size > 16383 ) dest-> size = 16383;
if ( dest-> name[0] == 0)
strcpy( dest-> name, "Default");
if ( dest-> pitch < fpDefault || dest-> pitch > fpFixed)
dest-> pitch = fpDefault;
if ( dest-> direction == C_NUMERIC_UNDEF)
dest-> direction = 0;
if ( dest-> style == C_NUMERIC_UNDEF)
dest-> style = 0;
return useSize && !useHeight;
}
int
Drawable_get_paint_state( Handle self)
{
if ( is_opt( optInDraw))
return psEnabled;
else if ( is_opt( optInDrawInfo))
return psInformation;
else
return psDisabled;
}
int
Drawable_get_bpp( Handle self)
{
gpARGS;
int ret;
gpENTER(0);
ret = apc_gp_get_bpp( self);
gpLEAVE;
return ret;
}
SV *
Drawable_linePattern( Handle self, Bool set, SV * pattern)
{
if ( set) {
STRLEN len;
unsigned char *pat = ( unsigned char *) SvPV( pattern, len);
if ( len > 255) len = 255;
apc_gp_set_line_pattern( self, pat, len);
} else {
unsigned char ret[ 256];
int len = apc_gp_get_line_pattern( self, ret);
return newSVpvn((char*) ret, len);
}
return nilSV;
}
Color
Drawable_get_nearest_color( Handle self, Color color)
{
gpARGS;
gpENTER(clInvalid);
color = apc_gp_get_nearest_color( self, color);
gpLEAVE;
return color;
}
Point
Drawable_resolution( Handle self, Bool set, Point resolution)
{
if ( set)
croak("Attempt to write read-only property %s", "Drawable::resolution");
resolution = apc_gp_get_resolution( self);
return resolution;
}
SV *
Drawable_get_physical_palette( Handle self)
{
gpARGS;
int i, nCol;
AV * av = newAV();
PRGBColor r;
gpENTER(newRV_noinc(( SV *) av));
r = apc_gp_get_physical_palette( self, &nCol);
gpLEAVE;
for ( i = 0; i < nCol; i++) {
av_push( av, newSViv( r[ i].b));
av_push( av, newSViv( r[ i].g));
av_push( av, newSViv( r[ i].r));
}
free( r);
return newRV_noinc(( SV *) av);
}
SV *
Drawable_get_font_abc( Handle self, int first, int last, Bool unicode)
{
int i;
AV * av;
PFontABC abc;
if ( first < 0) first = 0;
if ( last < 0) last = 255;
if ( !unicode) {
if ( first > 255) first = 255;
if ( last > 255) last = 255;
}
if ( first > last)
abc = nil;
else {
gpARGS;
gpENTER( newRV_noinc(( SV *) newAV()));
abc = apc_gp_get_font_abc( self, first, last, unicode );
gpLEAVE;
}
av = newAV();
if ( abc != nil) {
for ( i = 0; i <= last - first; i++) {
av_push( av, newSVnv( abc[ i]. a));
av_push( av, newSVnv( abc[ i]. b));
av_push( av, newSVnv( abc[ i]. c));
}
free( abc);
}
return newRV_noinc(( SV *) av);
}
SV *
Drawable_get_font_ranges( Handle self)
{
int count = 0;
unsigned long * ret;
AV * av = newAV();
gpARGS;
gpENTER( newRV_noinc(( SV *) av));
ret = apc_gp_get_font_ranges( self, &count);
gpLEAVE;
if ( ret) {
int i;
for ( i = 0; i < count; i++)
av_push( av, newSViv( ret[i]));
free( ret);
}
return newRV_noinc(( SV *) av);
}
SV *
Drawable_get_handle( Handle self)
{
char buf[ 256];
snprintf( buf, 256, "0x%08lx", apc_gp_get_handle( self));
return newSVpv( buf, 0);
}
int
Drawable_height( Handle self, Bool set, int height)
{
Point p = my-> get_size( self);
if ( !set)
return p. y;
p. y = height;
my-> set_size( self, p);
return height;
}
Point
Drawable_size ( Handle self, Bool set, Point size)
{
if ( set)
croak("Attempt to write read-only property %s", "Drawable::size");
size. x = var-> w;
size. y = var-> h;
return size;
}
int
Drawable_width( Handle self, Bool set, int width)
{
Point p = my-> get_size( self);
if ( !set)
return p. x;
p. x = width;
my-> set_size( self, p);
return width;
}
Bool
Drawable_put_image_indirect( Handle self, Handle image, int x, int y, int xFrom, int yFrom, int xDestLen, int yDestLen, int xLen, int yLen, int rop)
{
Bool ok;
if ( image == nilHandle) return false;
if ( xLen == xDestLen && yLen == yDestLen)
ok = apc_gp_put_image( self, image, x, y, xFrom, yFrom, xLen, yLen, rop);
else
ok = apc_gp_stretch_image( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
if ( !ok) perl_error();
return ok;
}
Bool
Drawable_text_out( Handle self, SV * text, int x, int y)
{
Bool ok;
STRLEN dlen;
char * c_text = SvPV( text, dlen);
Bool utf8 = prima_is_utf8_sv( text);
if ( utf8) dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen);
ok = apc_gp_text_out( self, c_text, x, y, dlen, utf8);
if ( !ok) perl_error();
return ok;
}
Point *
Drawable_polypoints( SV * points, char * procName, int mod, int * n_points)
{
AV * av;
int i, count;
Point * p;
if ( !SvROK( points) || ( SvTYPE( SvRV( points)) != SVt_PVAV)) {
warn("Invalid array reference passed to %s", procName);
return nil;
}
av = ( AV *) SvRV( points);
count = av_len( av) + 1;
if ( count % mod) {
warn("Drawable::%s: Number of elements in an array must be a multiple of %d",
procName, mod);
return nil;
}
count /= 2;
if ( count < 2) return nil;
if (!( p = allocn( Point, count))) return false;
for ( i = 0; i < count; i++)
{
SV** psvx = av_fetch( av, i * 2, 0);
SV** psvy = av_fetch( av, i * 2 + 1, 0);
if (( psvx == nil) || ( psvy == nil)) {
free( p);
warn("Array panic on item pair %d on Drawable::%s", i, procName);
return nil;
}
p[ i]. x = SvIV( *psvx);
p[ i]. y = SvIV( *psvy);
}
*n_points = count;
return p;
}
static Bool
polypoints( Handle self, SV * points, char * procName, int mod, Bool (*procPtr)(Handle,int,Point*))
{
int count;
Point * p;
Bool ret = false;
if (( p = Drawable_polypoints( points, procName, mod, &count))) {
ret = procPtr( self, count, p);
if ( !ret) perl_error();
free( p);
}
return ret;
}
Bool
Drawable_polyline( Handle self, SV * points)
{
return polypoints( self, points, "Drawable::polyline", 2, apc_gp_draw_poly);
}
Bool
Drawable_lines( Handle self, SV * points)
{
return polypoints( self, points, "Drawable::lines", 4, apc_gp_draw_poly2);
}
Bool
Drawable_fillpoly( Handle self, SV * points)
{
return polypoints( self, points, "Drawable::fillpoly", 2, apc_gp_fill_poly);
}
/*
*--------------------------------------------------------------
*
* TkBezierScreenPoints --
*
* Given four control points, create a larger set of XPoints
* for a Bezier spline based on the points.
*
* Results:
* The array at *xPointPtr gets filled in with numSteps XPoints
* corresponding to the Bezier spline defined by the four
* control points. Note: no output point is generated for the
* first input point, but an output point *is* generated for
* the last input point.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static void
TkBezierScreenPoints(
double control[], /* Array of coordinates for four
* control points: x0, y0, x1, y1,
* ... x3 y3. */
int numSteps, /* Number of curve points to
* generate. */
register Point *xPointPtr) /* Where to put new points. */
{
int i;
double u, u2, u3, t, t2, t3;
for (i = 1; i <= numSteps; i++, xPointPtr++) {
t = ((double) i)/((double) numSteps);
t2 = t*t;
t3 = t2*t;
u = 1.0 - t;
u2 = u*u;
u3 = u2*u;
xPointPtr-> x = control[0]*u3 + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3;
xPointPtr-> y = control[1]*u3 + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3;
}
}
/*
*--------------------------------------------------------------
*
* TkMakeBezierCurve --
*
* Given a set of points, create a new set of points that fit
* parabolic splines to the line segments connecting the original
* points.
*
* Note: in spite of this procedure's name, it does *not* generate
* Bezier curves. Since only three control points are used for
* each curve segment, not four, the curves are actually just
* parabolic.
*
* Results:
* xPoints array is always filled
* in. The return value is the number of points placed in the
* array. Note: if the first and last points are the same, then
* a closed curve is generated.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static int
TkMakeBezierCurve(
int *pointPtr, /* Array of input coordinates: x0,
* y0, x1, y1, etc.. */
int numPoints, /* Number of points at pointPtr. */
int numSteps, /* Number of steps to use for each
* spline segments (determines
* smoothness of curve). */
Point xPoints[]) /* Array of Points to fill in (e.g.
* for display. NULL means don't
* fill in any Points. */
{
int closed, outputPoints, i;
int numCoords = numPoints*2;
double control[8];
/*
* If the curve is a closed one then generate a special spline
* that spans the last points and the first ones. Otherwise
* just put the first point into the output.
*/
if (!pointPtr) {
/* Of pointPtr == NULL, this function returns an upper limit.
* of the array size to store the coordinates. This can be
* used to allocate storage, before the actual coordinates
* are calculated. */
return 1 + numPoints * numSteps;
}
outputPoints = 0;
if ((pointPtr[0] == pointPtr[numCoords-2])
&& (pointPtr[1] == pointPtr[numCoords-1])) {
closed = 1;
control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
if (xPoints != NULL) {
xPoints-> x = control[0];
xPoints-> y = control[1];
TkBezierScreenPoints( control, numSteps, xPoints+1);
xPoints += numSteps+1;
}
outputPoints += numSteps+1;
} else {
closed = 0;
if (xPoints != NULL) {
xPoints->x = pointPtr[0];
xPoints->y = pointPtr[1];
xPoints += 1;
}
outputPoints += 1;
}
for (i = 2; i < numPoints; i++, pointPtr += 2) {
/*
* Set up the first two control points. This is done
* differently for the first spline of an open curve
* than for other cases.
*/
if ((i == 2) && !closed) {
control[0] = pointPtr[0];
control[1] = pointPtr[1];
control[2] = 0.333*pointPtr[0] + 0.667*pointPtr[2];
control[3] = 0.333*pointPtr[1] + 0.667*pointPtr[3];
} else {
control[0] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
control[1] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
control[2] = 0.167*pointPtr[0] + 0.833*pointPtr[2];
control[3] = 0.167*pointPtr[1] + 0.833*pointPtr[3];
}
/*
* Set up the last two control points. This is done
* differently for the last spline of an open curve
* than for other cases.
*/
if ((i == (numPoints-1)) && !closed) {
control[4] = .667*pointPtr[2] + .333*pointPtr[4];
control[5] = .667*pointPtr[3] + .333*pointPtr[5];
control[6] = pointPtr[4];
control[7] = pointPtr[5];
} else {
control[4] = .833*pointPtr[2] + .167*pointPtr[4];
control[5] = .833*pointPtr[3] + .167*pointPtr[5];
control[6] = 0.5*pointPtr[2] + 0.5*pointPtr[4];
control[7] = 0.5*pointPtr[3] + 0.5*pointPtr[5];
}
/*
* If the first two points coincide, or if the last
* two points coincide, then generate a single
* straight-line segment by outputting the last control
* point.
*/
if (((pointPtr[0] == pointPtr[2]) && (pointPtr[1] == pointPtr[3]))
|| ((pointPtr[2] == pointPtr[4])
&& (pointPtr[3] == pointPtr[5]))) {
if (xPoints != NULL) {
xPoints[0].x = control[6];
xPoints[0].y = control[7];
xPoints++;
}
outputPoints += 1;
continue;
}
/*
* Generate a Bezier spline using the control points.
*/
if (xPoints != NULL) {
TkBezierScreenPoints(control, numSteps, xPoints);
xPoints += numSteps;
}
outputPoints += numSteps;
}
return outputPoints;
}
#define STATIC_ARRAY_SIZE 200
static Bool
plot_spline( Handle self, int count, Point * points, Bool fill)
{
Bool ret;
int array_size;
Point static_array[STATIC_ARRAY_SIZE], *array;
array_size = TkMakeBezierCurve( NULL, count, var-> splinePrecision, NULL);
if ( array_size >= STATIC_ARRAY_SIZE) {
if ( !( array = malloc( array_size * sizeof( Point)))) {
warn("Not enough memory");
return false;
}
} else
array = static_array;
array_size = TkMakeBezierCurve((int*) points, count, var-> splinePrecision, array);
if ( fill && ( my-> fillpoly == Drawable_fillpoly)) {
ret = apc_gp_fill_poly( self, array_size, array);
if ( !ret) perl_error();
} else if ( !fill && ( my-> polyline == Drawable_polyline)) {
ret = apc_gp_draw_poly( self, array_size, array);
if ( !ret) perl_error();
} else {
int i;
AV * av = newAV();
SV * sv = newRV(( SV*) av);
for ( i = 0; i < array_size; i++) {
av_push( av, newSViv( array[i]. x));
av_push( av, newSViv( array[i]. y));
}
ret = fill ?
my-> fillpoly( self, sv) :
my-> polyline( self, sv);
sv_free( sv);
}
if ( array != static_array) free( array);
return ret;
}
static Bool
spline( Handle self, int count, Point * points)
{
return plot_spline( self, count, points, false);
}
static Bool
fill_spline( Handle self, int count, Point * points)
{
return plot_spline( self, count, points, true);
}
Bool
Drawable_spline( Handle self, SV * points)
{
return polypoints( self, points, "Drawable::spline", 2, spline);
}
Bool
Drawable_fill_spline( Handle self, SV * points)
{
return polypoints( self, points, "Drawable::fill_spline", 2, fill_spline);
}
SV *
Drawable_render_spline( SV * obj, SV * points, int precision)
{
int i, n_p, array_size;
Point static_array[STATIC_ARRAY_SIZE], *array, *p;
AV * av;
if ( precision < 0) {
Handle self;
self = gimme_the_mate( obj);
precision = self ? var-> splinePrecision : 24;
}
av = newAV();
p = Drawable_polypoints( points, "Drawable::render_spline", 2, &n_p);
if ( p) {
array_size = TkMakeBezierCurve( NULL, n_p, precision, NULL);
if ( array_size >= STATIC_ARRAY_SIZE) {
if ( !( array = malloc( array_size * sizeof( Point)))) {
warn("Not enough memory");
free( p);
return newRV_noinc(( SV *) av);
}
} else
array = static_array;
array_size = TkMakeBezierCurve((int*) p, n_p, precision, array);
for ( i = 0; i < array_size; i++) {
av_push( av, newSViv( array[i]. x));
av_push( av, newSViv( array[i]. y));
}
if ( array != static_array) free( array);
free( p);
}
return newRV_noinc(( SV *) av);
}
int
Drawable_get_text_width( Handle self, SV * text, Bool addOverhang)
{
gpARGS;
int res;
STRLEN dlen;
char * c_text = SvPV( text, dlen);
Bool utf8 = prima_is_utf8_sv( text);
if ( utf8) dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen);
gpENTER(0);
res = apc_gp_get_text_width( self, c_text, dlen, addOverhang, utf8);
gpLEAVE;
return res;
}
SV *
Drawable_get_text_box( Handle self, SV * text)
{
gpARGS;
Point * p;
AV * av;
int i;
STRLEN dlen;
char * c_text = SvPV( text, dlen);
Bool utf8 = prima_is_utf8_sv( text);
if ( utf8) dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen);
gpENTER( newRV_noinc(( SV *) newAV()));
p = apc_gp_get_text_box( self, c_text, dlen, utf8);
gpLEAVE;
av = newAV();
if ( p) {
for ( i = 0; i < 5; i++) {
av_push( av, newSViv( p[ i]. x));
av_push( av, newSViv( p[ i]. y));
};
free( p);
}
return newRV_noinc(( SV *) av);
}
static PFontABC
query_abc_range( Handle self, TextWrapRec * t, unsigned int base)
{
PFontABC abc;
/* find if present in cache */
if ( t-> utf8_text) {
if ( *(t-> unicode)) {
int i;
PList p;
if (( p = *(t-> unicode)))
for ( i = 0; i < p-> count; i += 2)
if (( unsigned int) p-> items[ i] == base)
return ( PFontABC) p-> items[i + 1];
}
} else
if ( *( t-> ascii)) return *(t-> ascii);
/* query ABC information */
if ( !self) {
abc = apc_gp_get_font_abc( self, base * 256, base * 256 + 255, t-> utf8_text);
if ( !abc) return nil;
} else if ( my-> get_font_abc == Drawable_get_font_abc) {
gpARGS;
gpENTER(nil);
abc = apc_gp_get_font_abc( self, base * 256, base * 256 + 255, t-> utf8_text);
gpLEAVE;
if ( !abc) return nil;
} else {
SV * sv;
if ( !( abc = malloc( 256 * sizeof( FontABC)))) return nil;
sv = my-> get_font_abc( self, base * 256, base * 256 + 255, t-> utf8_text);
if ( SvOK( sv) && SvROK( sv) && SvTYPE( SvRV( sv)) == SVt_PVAV) {
AV * av = ( AV*) SvRV( sv);
int i, j = 0, n = av_len( av) + 1;
if ( n > 256 * 3) n = 256 * 3;
n = ( n / 3) * 3;
if ( n < 256) memset( abc, 0, 256 * sizeof( FontABC));
for ( i = 0; i < n; i += 3) {
SV ** holder = av_fetch( av, i, 0);
if ( holder) abc[j]. a = ( float) SvNV( *holder);
holder = av_fetch( av, i + 1, 0);
if ( holder) abc[j]. b = ( float) SvNV( *holder);
holder = av_fetch( av, i + 2, 0);
if ( holder) abc[j]. c = ( float) SvNV( *holder);
j++;
}
} else
memset( abc, 0, 256 * sizeof( FontABC));
sv_free( sv);
}
/* store in cache */
if ( t-> utf8_text) {
PList p;
if ( !*(t-> unicode))
*(t-> unicode) = plist_create( 8, 8);
if (( p = *(t-> unicode))) {
list_add( p, ( Handle) base);
list_add( p, ( Handle) abc);
} else {
free( abc);
return nil;
}
} else
*(t-> ascii) = abc;
return abc;
}
static Bool
precalc_abc_buffer( PFontABC src, float * width, PFontABC dest)
{
int i;
if ( !dest) return false;
for ( i = 0; i < 256; i++) {
width[i] = src[i]. a + src[i]. b + src[i]. c;
dest[i]. a = ( src[i]. a < 0) ? - src[i]. a : 0;
dest[i]. b = src[i]. b;
dest[i]. c = ( src[i]. c < 0) ? - src[i]. c : 0;
}
return true;
}
static Bool
add_wrapped_text( TextWrapRec * t, int start, int utfstart, int end, int utfend,
int tildeIndex, int * tildePos, int * tildeLPos, int * tildeLine,
char *** lArray, int * lSize)
{
int l = end - start;
char *c = nil;
if (!( t-> options & twReturnChunks)) {
if ( !( c = allocs( l + 1))) return false;
memcpy( c, t-> text + start, l);
c[ l] = 0;
}
if ( tildeIndex >= 0 && tildeIndex >= start && tildeIndex < end) {
*tildeLine = t-> t_line = t-> count;
*tildePos = *tildeLPos = tildeIndex - start;
if ( tildeIndex == end - 1) {
t-> t_line++;
tildeLPos = 0;
}
}
if ( t-> count == *lSize) {
char ** n = allocn( char*, *lSize + 16);
if ( !n) return false;
memcpy( n, *lArray, sizeof( char*) * (*lSize));
*lSize += 16;
free( *lArray);
*lArray = n;
}
if ( t-> options & twReturnChunks) {
(*lArray)[ t-> count++] = INT2PTR(char*,utfstart);
(*lArray)[ t-> count++] = INT2PTR(char*,utfend - utfstart);
} else
(*lArray)[ t-> count++] = c;
return true;
}
char **
Drawable_do_text_wrap( Handle self, TextWrapRec * t)
{
unsigned int base = 0x10000000;
float width[256];
FontABC abc[256];
int start = 0, utf_start = 0, split_start = -1, split_end = -1, i, utf_p, utf_split = -1;
float w = 0, inc = 0;
char **ret;
Bool wasTab = 0, reassign_w = 1;
Bool doWidthBreak = t-> width >= 0;
int tildeIndex = -100, tildeLPos = 0, tildeLine = 0, tildePos = 0, tildeOffset = 0, lSize = 16;
int spaceWidth = 0, spaceC = 0, spaceOK = 0;
#define lAdd(end, utfend) \
if ( !add_wrapped_text( t, start, utf_start, end, utfend, tildeIndex, \
&tildePos, &tildeLPos, &tildeLine, &ret, &lSize)) return ret;\
start = end; \
utf_start = utfend; \
if (( t-> options & twReturnFirstLineLength) == twReturnFirstLineLength) return ret
t-> count = 0;
if (!( ret = allocn( char*, lSize))) return nil;
/* determining ~ character location */
if ( t-> options & twCalcMnemonic)
for ( i = 0; i < t-> textLen - 1; i++)
if ( t-> text[ i] == '~') {
unsigned char c = t-> text[ i + 1];
if ( c == '~' || c < ' ') {
i++;
continue;
} else {
tildeIndex = i;
break;
}
}
/* process UV chars */
for ( i = 0, utf_p = 0; i < t-> textLen; utf_p++) {
UV uv;
float winc;
int p = i;
if ( t-> utf8_text) {
STRLEN len;
#if PERL_PATCHLEVEL >= 16
uv = utf8_to_uvchr_buf(( U8*) t-> text + i, ( U8*) t-> text + t-> textLen, &len);
#else
uv = utf8_to_uvchr(( U8*) t-> text + i, &len);
#endif
i += len;
if ( len == 0 ) break;
} else
uv = (( unsigned char *)(t-> text))[i++];
if ( uv / 256 != base)
if ( !precalc_abc_buffer( query_abc_range( self, t, base = uv / 256), width, abc))
return ret;
if ( reassign_w) w = abc[ uv & 0xff]. a;
reassign_w = 0;
switch ( uv ) {
case '\t':
split_start = p; split_end = i; utf_split = utf_p;
if (!( t-> options & twCalcTabs)) goto _default;
if ( t-> options & twSpaceBreak) {
lAdd( p, utf_p);
start = i;
utf_start++;
reassign_w = 1;
continue;
}
if ( !spaceOK) {
PFontABC s = query_abc_range( self, t, 0);
if ( !s) return ret;
spaceWidth = (s[' '].a + s[' '].b + s[' '].c) * t-> tabIndent;
spaceC = (s[' '].c < 0) ? - s[' ']. c : 0;
spaceOK = 1;
}
winc = spaceWidth;
inc = spaceC;
wasTab = true;
break;
case '\n':
case 0x2028:
case 0x2029:
split_start = p; split_end = i; utf_split = utf_p;
if (!( t-> options & twNewLineBreak)) goto _default;
lAdd( p, utf_p);
start = i;
utf_start++;
reassign_w = 1;
continue;
case ' ':
split_start = p; split_end = i; utf_split = utf_p;
if (!( t-> options & twSpaceBreak)) goto _default;
lAdd( p, utf_p);
start = i;
utf_start++;
reassign_w = 1;
continue;
case '~':
if ( p == tildeIndex ) {
tildeOffset = w;
inc = winc = 0;
break;
}
_default: default:
winc = width[ uv & 0xff];
inc = abc[ uv & 0xff]. c;
}
if ( doWidthBreak && w + winc + inc > t-> width) {
if (( p == start) || (( p == start - 1) && ( p - 1 == tildeIndex))) {
/* case when even single char cannot be fit in */
if ( t-> options & twBreakSingle) {
/* do not return anything in this case */
int j;
if (!( t-> options & twReturnChunks)) {
for ( j = 0; j < t-> count; j++) free( ret[ j]);
ret[ 0] = duplicate_string("");
}
t-> count = 0;
return ret;
}
/* or push this character disregarding the width */
lAdd( i, utf_p + 1);
} else { /* normal break condition */
/* checking if break was at word boundary */
if ( t-> options & twWordBreak) {
if ( start <= split_start) {
lAdd( split_start, utf_split );
i = start = split_end;
utf_start = utf_split + 1;
utf_p = utf_split;
w = 0;
continue;
} else if ( t-> options & twBreakSingle) {
/* cannot be split, return nothing */
int j;
if (!( t-> options & twReturnChunks)) {
for ( j = 0; j < t-> count; j++) free( ret[ j]);
ret[ 0] = duplicate_string("");
}
t-> count = 0;
return ret;
}
}
/* repeat again */
lAdd( p, utf_p );
i = start = p;
utf_start = utf_p;
utf_p--;
}
w = 0;
continue;
} else
w += winc;
}
/* adding or skipping last line */
if ( t-> textLen - start > 0 || t-> count == 0) lAdd( t-> textLen, t-> utf8_textLen);
/* removing ~ and determining it's location */
if ( tildeIndex >= 0 && !(t-> options & twReturnChunks)) {
PFontABC abc;
char *l = ret[ tildeLine];
t-> t_char = t-> text + tildePos + 1;
if ( t-> options & twCollapseTilde)
memmove( l + tildePos, l + tildePos + 1, strlen( l) - tildePos);
abc = query_abc_range( self, t, 0) + '~';
w = tildeOffset;
t-> t_start = w - 1;
t-> t_end = w + abc->a + abc->b + abc->c;
} else {
t-> t_start = t-> t_end = t-> t_line = C_NUMERIC_UNDEF;
}
/* expanding tabs */
if (( t-> options & twExpandTabs) && !(t-> options & twReturnChunks) && wasTab) {
for ( i = 0; i < t-> count; i++) {
int tabs = 0, len = 0;
char *substr = ret[ i], *n;
while (*substr) {
if ( *substr == '\t') tabs++;
substr++;
len++;
}
if ( tabs == 0) continue;
if ( !( n = allocs( len + tabs * t-> tabIndent + 1)))
return ret;
len = 0;
substr = ret[ i];
while ( *substr) {
if ( *substr == '\t') {
int j = t-> tabIndent;
while ( j--) n[ len++] = ' ';
} else
n[ len++] = *substr;
substr++;
}
free( ret[ i]);
n[ len] = 0;
ret[ i] = n;
}
}
return ret;
}
SV*
Drawable_text_wrap( Handle self, SV * text, int width, int options, int tabIndent)
{
TextWrapRec t;
Bool retChunks;
char** c;
int i;
AV * av;
STRLEN tlen;
t. text = SvPV( text, tlen);
t. utf8_text = prima_is_utf8_sv( text);
if ( t. utf8_text) {
t. utf8_textLen = prima_utf8_length( t. text);
t. textLen = utf8_hop(( U8*) t. text, t. utf8_textLen) - (U8*) t. text;
} else {
t. utf8_textLen = t. textLen = tlen;
}
t. width = ( width < 0) ? 0 : width;
t. tabIndent = ( tabIndent < 0) ? 0 : tabIndent;
t. options = options;
retChunks = t. options & twReturnChunks;
t. ascii = &var-> font_abc_ascii;
t. unicode = &var-> font_abc_unicode;
t. t_char = nil;
c = Drawable_do_text_wrap( self, &t);
if (( t. options & twReturnFirstLineLength) == twReturnFirstLineLength) {
IV rlen = 0;
if ( c) {
if ( t. count > 0) rlen = PTR2IV(c[1]);
free( c);
}
return newSViv( rlen);
}
if ( !c) return nilSV;
av = newAV();
for ( i = 0; i < t. count; i++) {
SV * sv = retChunks ? newSViv( PTR2IV(c[i])) : newSVpv( c[ i], 0);
if ( !retChunks) {
if ( t. utf8_text) SvUTF8_on( sv);
free( c[i]);
}
av_push( av, sv);
}
free( c);
if ( t. options & ( twCalcMnemonic | twCollapseTilde)) {
HV * profile = newHV();
SV * sv_char;
if ( t. t_char) {
STRLEN len = t. utf8_text ? utf8_hop(( U8*) t. t_char, 1) - ( U8*) t. t_char : 1;
sv_char = newSVpv( t. t_char, len);
if ( t. utf8_text) SvUTF8_on( sv_char);
pset_i( tildeStart, t. t_start);
pset_i( tildeEnd, t. t_end);
pset_i( tildeLine, t. t_line);
} else {
sv_char = newSVsv( nilSV);
pset_sv( tildeStart, nilSV);
pset_sv( tildeEnd, nilSV);
pset_sv( tildeLine, nilSV);
}
pset_sv_noinc( tildeChar, sv_char);
av_push( av, newRV_noinc(( SV *) profile));
}
return newRV_noinc(( SV *) av);
}
PRGBColor
read_palette( int * palSize, SV * palette)
{
AV * av;
int i, count;
Byte * buf;
if ( !SvROK( palette) || ( SvTYPE( SvRV( palette)) != SVt_PVAV)) {
*palSize = 0;
return nil;
}
av = (AV *) SvRV( palette);
count = av_len( av) + 1;
*palSize = count / 3;
count = *palSize * 3;
if ( count == 0) return nil;
if ( !( buf = allocb( count))) return nil;
for ( i = 0; i < count; i++)
{
SV **itemHolder = av_fetch( av, i, 0);
if ( itemHolder == nil)
return ( PRGBColor) buf;
buf[ i] = SvIV( *itemHolder);
}
return ( PRGBColor) buf;
}
Color
Drawable_backColor( Handle self, Bool set, Color color)
{
if (!set) return apc_gp_get_back_color( self);
apc_gp_set_back_color( self, color);
return color;
}
Color
Drawable_color( Handle self, Bool set, Color color)
{
if (!set) return apc_gp_get_color( self);
apc_gp_set_color( self, color);
return color;
}
Rect
Drawable_clipRect( Handle self, Bool set, Rect clipRect)
{
if ( !set)
return apc_gp_get_clip_rect( self);
apc_gp_set_clip_rect( self, clipRect);
return clipRect;
}
Bool
Drawable_fillWinding( Handle self, Bool set, Bool fillWinding)
{
if (!set) return apc_gp_get_fill_winding( self);
apc_gp_set_fill_winding( self, fillWinding);
return fillWinding;
}
int
Drawable_lineEnd( Handle self, Bool set, int lineEnd)
{
if (!set) return apc_gp_get_line_end( self);
apc_gp_set_line_end( self, lineEnd);
return lineEnd;
}
int
Drawable_lineJoin( Handle self, Bool set, int lineJoin)
{
if (!set) return apc_gp_get_line_join( self);
apc_gp_set_line_join( self, lineJoin);
return lineJoin;
}
int
Drawable_lineWidth( Handle self, Bool set, int lineWidth)
{
if (!set) return apc_gp_get_line_width( self);
apc_gp_set_line_width( self, lineWidth);
return lineWidth;
}
SV *
Drawable_palette( Handle self, Bool set, SV * palette)
{
int colors;
if ( var-> stage > csFrozen) return nilSV;
colors = var-> palSize;
if ( set) {
free( var-> palette);
var-> palette = read_palette( &var-> palSize, palette);
if ( colors == 0 && var-> palSize == 0) return nilSV; /* do not bother apc */
apc_gp_set_palette( self);
} else {
AV * av = newAV();
int i;
Byte * pal = ( Byte*) var-> palette;
for ( i = 0; i < colors * 3; i++) av_push( av, newSViv( pal[ i]));
return newRV_noinc(( SV *) av);
}
return nilSV;
}
SV *
Drawable_pixel( Handle self, Bool set, int x, int y, SV * color)
{
if (!set)
return newSViv( apc_gp_get_pixel( self, x, y));
apc_gp_set_pixel( self, x, y, SvIV( color));
return nilSV;
}
Handle
Drawable_region( Handle self, Bool set, Handle mask)
{
if ( var-> stage > csFrozen) return nilHandle;
if ( set) {
if ( mask && !kind_of( mask, CImage)) {
warn("Illegal object reference passed to Drawable::region");
return nilHandle;
}
if ( mask && (( PImage( mask)-> type & imBPP) != imbpp1)) {
Handle i = CImage( mask)-> dup( mask);
++SvREFCNT( SvRV( PImage( i)-> mate));
CImage( i)-> set_conversion( i, ictNone);
CImage( i)-> set_type( i, imBW);
apc_gp_set_region( self, i);
--SvREFCNT( SvRV( PImage( i)-> mate));
Object_destroy( i);
} else
apc_gp_set_region( self, mask);
} else if ( apc_gp_get_region( self, nilHandle)) {
HV * profile = newHV();
Handle i = Object_create( "Prima::Image", profile);
sv_free(( SV *) profile);
apc_gp_get_region( self, i);
--SvREFCNT( SvRV((( PAnyObject) i)-> mate));
return i;
}
return nilHandle;
}
int
Drawable_rop( Handle self, Bool set, int rop)
{
if (!set) return apc_gp_get_rop( self);
apc_gp_set_rop( self, rop);
return rop;
}
int
Drawable_rop2( Handle self, Bool set, int rop2)
{
if (!set) return apc_gp_get_rop2( self);
apc_gp_set_rop2( self, rop2);
return rop2;
}
int
Drawable_splinePrecision( Handle self, Bool set, int splinePrecision)
{
if ( !set) return var-> splinePrecision;
if ( splinePrecision < 1) return -1;
var-> splinePrecision = splinePrecision;
return splinePrecision;
}
Bool
Drawable_textOpaque( Handle self, Bool set, Bool opaque)
{
if (!set) return apc_gp_get_text_opaque( self);
apc_gp_set_text_opaque( self, opaque);
return opaque;
}
Bool
Drawable_textOutBaseline( Handle self, Bool set, Bool textOutBaseline)
{
if (!set) return apc_gp_get_text_out_baseline( self);
apc_gp_set_text_out_baseline( self, textOutBaseline);
return textOutBaseline;
}
Point
Drawable_translate( Handle self, Bool set, Point translate)
{
if (!set) return apc_gp_get_transform( self);
apc_gp_set_transform( self, translate. x, translate. y);
return translate;
}
SV *
Drawable_fillPattern( Handle self, Bool set, SV * svpattern)
{
int i;
if ( !set) {
AV * av;
FillPattern * fp = apc_gp_get_fill_pattern( self);
if ( !fp) return nilSV;
av = newAV();
for ( i = 0; i < 8; i++) av_push( av, newSViv(( int) (*fp)[i]));
return newRV_noinc(( SV *) av);
} else {
if ( SvROK( svpattern) && ( SvTYPE( SvRV( svpattern)) == SVt_PVAV)) {
FillPattern fp;
AV * av = ( AV *) SvRV( svpattern);
if ( av_len( av) != 7) {
warn("Illegal fillPattern passed to Drawable::fillPattern");
return nilSV;
}
for ( i = 0; i < 8; i++) {
SV ** holder = av_fetch( av, i, 0);
if ( !holder) {
warn("Array panic on Drawable::fillPattern");
return nilSV;
}
fp[ i] = SvIV( *holder);
}
apc_gp_set_fill_pattern( self, fp);
} else {
int id = SvIV( svpattern);
if (( id < 0) || ( id > fpMaxId)) {
warn("fillPattern index out of range passed to Drawable::fillPattern");
return nilSV;
}
apc_gp_set_fill_pattern( self, fillPatterns[ id]);
}
}
return nilSV;
}
Font
Drawable_get_font( Handle self)
{
return var-> font;
}
void
Drawable_set_font( Handle self, Font font)
{
clear_font_abc_caches( self);
apc_font_pick( self, &font, &var-> font);
apc_gp_set_font( self, &var-> font);
}
#ifdef __cplusplus
}
#endif