/*- * 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: Drawable.c,v 1.97 2005/03/30 19:33:43 dk Exp $ */ #include "apricot.h" #include "Drawable.h" #include "Image.h" #include #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) { 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("RTC0059: Array panic on 'translate'"); holder = av_fetch( av, 1, 0); if ( holder) tr.y = SvIV( *holder); else warn("RTC0059: 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) { 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("RTC0059: Array panic on 'translate'"); holder = av_fetch( av, 1, 0); if ( holder) tr.y = SvIV( *holder); else warn("RTC0059: 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 1; else if ( is_opt( optInDrawInfo)) return 2; else return 0; } 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) { if ( image == nilHandle) return false; if ( xLen == xDestLen && yLen == yDestLen) return apc_gp_put_image( self, image, x, y, xFrom, yFrom, xLen, yLen, rop); else return apc_gp_stretch_image( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop); } Bool Drawable_text_out( Handle self, SV * text, int x, int y) { STRLEN dlen; char * c_text = SvPV( text, dlen); Bool utf8 = SvUTF8( text); if ( utf8) dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen); return apc_gp_text_out( self, c_text, x, y, dlen, utf8); } 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("RTC0050: Invalid array reference passed to %s", procName); return nil; } av = ( AV *) SvRV( points); count = av_len( av) + 1; if ( count % mod) { warn("RTC0051: 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("RTC0052: 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); 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); } else if ( !fill && ( my-> polyline == Drawable_polyline)) { ret = apc_gp_draw_poly( self, array_size, array); } 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 = SvUTF8( 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 = SvUTF8( 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; uv = utf8_to_uvchr(( U8*) t-> text + i, &len); i += len; } 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)) { UV uv; STRLEN len; 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); l = ret[ t-> t_line] + tildeLPos; uv = t-> utf8_text ? utf8_to_uvchr(( U8*) l, &len) : *((unsigned char*)l); abc = query_abc_range( self, t, uv / 256) + (uv & 0xff); 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 = SvUTF8( 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("RTC005A: 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("RTC0056: Illegal fillPattern passed to Drawable::fillPattern"); return nilSV; } for ( i = 0; i < 8; i++) { SV ** holder = av_fetch( av, i, 0); if ( !holder) { warn("RTC0057: 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("RTC0058: 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