/* Context related functions. Copyright (C) 2001 Johan Rydberg. All Rights Reserved. This file is part of Crust. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include "GFContext.h" #include "priv.h" #define MAX_OPACITY(X) MIN (0x10000, (int) (X * 0x10000)) CFTypeID _GFContextTypeID; /* CoreFoundation runtime class for GFContext. */ CFRuntimeClass _GFContextRuntimeClass = { 0, /* version */ "GFContext", /* Name of class. */ sizeof (struct GFContext), /* Instance size. */ 0, /* Init function. */ 0 /* Final function. */ }; static int alpha_info_to_libart_alpha_pos (GFImageAlphaInfo alpha_info) { if (alpha_info == kCGImageAlphaSeparateLast) return ART_ALPHA_LAST; else if (alpha_info == kCGImageAlphaPremultipliedLast) return ART_ALPHA_LAST; else if (alpha_info == kCGImageAlphaNoneLast) return ART_ALPHA_LAST; else if (alpha_info == kCGImageAlphaNone) return ART_ALPHA_LAST; return ART_ALPHA_FIRST; } static int alpha_info_to_libart_alpha_info (GFImageAlphaInfo alpha_info) { if (alpha_info == kCGImageAlphaPremultipliedLast || alpha_info == kCGImageAlphaPremultipliedFirst) return ART_ALPHA_PREMUL; else if (alpha_info == kCGImageAlphaSeparateLast || alpha_info == kCGImageAlphaSeparateFirst) return ART_ALPHA_SEPARATE; /* ??? ART_ALPHA_NONE here!!! */ return ART_ALPHA_SEPARATE; } static void initialize_context_runtime (void) __attribute__ ((constructor)); /* Initialize runtime class for GFContext. This has a "constructor" attribute so that is should be called at initialization time. */ static void initialize_context_runtime (void) { _GFContextTypeID = CFRuntimeRegisterClass (&_GFContextRuntimeClass); } /* Internal function to allocate a new context instance. */ GFContextRef _GFContextAllocate (void) { return (GFContextRef) CFRuntimeCreateInstance (_GFContextTypeID); } /* Retain a reference to CONTEXT. */ void GFContextRetain (GFContextRef context) { CFRetain (context); } /* Release a reference to CONTEXT. */ void GFContextRelease (GFContextRef context) { CFRelease (context); } /* Begin a new path. The old path is destroyed. */ void GFContextBeginPath (GFContextRef context) { /* We just truncate the number of vectors. */ context->vpath_n = 0; } /* Close current subpath. */ void GFContextClosePath (GFContextRef context) { int vpath_subpath = context->vpath_subpath; if (context->vpath_closed == true) return; art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_LINETO, context->vpath[vpath_subpath].x, context->vpath[vpath_subpath].y); context->vpath_closed = true; } /* Move current point to (X, Y). Opens a new subpath. */ void GFContextMoveToPoint (GFContextRef context, float x, float y) { /* Add point to path. */ if (context->vpath_closed == true) art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_MOVETO_OPEN, x, y); else art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_MOVETO, x, y); } /* Add a line from current point to (X, Y). */ void GFContextAddLineToPoint (GFContextRef context, float x, float y) { /* Add point to path. */ art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_LINETO, x, y); } /* Creates a new subpath according to RECT. */ void GFContextAddRect (GFContextRef context, GFRect rect) { GFContextMoveToPoint (context, rect.origin.x, rect.origin.y); GFContextAddLineToPoint (context, rect.origin.x, rect.origin.y + rect.size.height); GFContextAddLineToPoint (context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); GFContextAddLineToPoint (context, rect.origin.x + rect.size.width, rect.origin.y); GFContextClosePath (context); } /* Set alpha value for context to VALUE. */ void GFContextSetAlpha (GFContextRef context, float value) { context->gstate->alpha_value = value; } /* Set fill color to VALUE. Number of components in VALUE is specified by current fill color space. */ void GFContextSetFillColor (GFContextRef context, float value[]) { int i; for (i = 0; i < context->gstate->fill_space->n_components; i++) context->gstate->fill_color [i] = value [i]; } /* Set CONTEXT's fill color space to COLORSPACE. */ void GFContextSetFillColorSpace (GFContextRef context, GFColorSpaceRef colorspace) { GFColorSpaceRelease (context->gstate->fill_space); context->gstate->fill_space = colorspace; } /* Set stoke color to VALUE. Number of components in VALUE is specified by current stroke color space. */ void GFContextSetStrokeColor (GFContextRef context, float value[]) { int i; for (i = 0; i < context->gstate->stroke_space->n_components; i++) context->gstate->stroke_color [i] = value [i]; } /* Set CONTEXT's stroke color space to COLORSPACE. */ void GFContextSetStrokeColorSpace (GFContextRef context, GFColorSpaceRef colorspace) { GFColorSpaceRelease (context->gstate->stroke_space); context->gstate->stroke_space = colorspace; } /* Convenience function to set fill color space to DeviceRGB and current fill color to VALUE. */ void GFContextSetRGBFillColor (GFContextRef context, float r, float g, float b, float alpha) { float value_array [3] = { r, g, b }; GFContextSetFillColorSpace (context, GFColorSpaceCreateDeviceRGB ()); GFContextSetFillColor (context, value_array); GFContextSetAlpha (context, alpha); } /* Convenience function to set stroke color space to DeviceRGB and current stroke color to VALUE. */ void GFContextSetRGBStrokeColor (GFContextRef context, float r, float g, float b, float alpha) { float value_array [3] = { r, g, b }; GFContextSetStrokeColorSpace (context, GFColorSpaceCreateDeviceRGB ()); GFContextSetStrokeColor (context, value_array); GFContextSetAlpha (context, alpha); } /* Set line width in current graphic state to LINE_WIDTH. */ void GFContextSetLineWidth (GFContextRef context, float line_width) { GFGStateRef gstate = context->gstate; gstate->line_width = line_width; } /* Set line caption in current graphic state to LINE_CAP. */ void GFContextSetLineCap (GFContextRef context, int line_cap) { GFGStateRef gstate = context->gstate; gstate->line_cap = line_cap; } /* Set line join in current graphic state to LINE_JOIN. */ void GFContextSetLineJoin (GFContextRef context, int line_join) { GFGStateRef gstate = context->gstate; gstate->line_join = line_join; } /* Add a bezier curve from current point to (X0, Y0). (X1, Y1) and (X2, Y2) is the two control points. */ void GFContextAddCurveToPoint (GFContextRef context, float x1, float y1, float x2, float y2, float x0, float y0) { ArtBpath bez; bez.code = ART_CURVETO; bez.x3 = x0; bez.y3 = y0; bez.x1 = x1; bez.y1 = y1; bez.x2 = x2; bez.y2 = y2; context->bpath [context->bpath_n++] = bez; art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_CURVETO, x0, y0); } /* Add a quadratic curve from current point to (X0, Y0). (X1, Y1) is the control point. */ void GFContextAddQuadCurveToPoint (GFContextRef context, float x1, float y1, float x0, float y0) { ArtBpath bez; bez.code = ART_CURVETO; bez.x3 = x0; bez.y3 = y0; bez.x1 = x1; bez.y1 = y1; bez.x2 = x1; bez.y2 = y1; context->bpath [context->bpath_n++] = bez; art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_CURVETO, x0, y0); } /* Draw path in CONTEXT according to drawing mode specified in MODE. We first constructs a final path of all lines and curves. The final path is flipped to device coordinates. */ void GFContextDrawPath (GFContextRef context, GFPathDrawingMode mode) { int final_n = 0, final_n_max, i, bez_index = 0, n; GFGStateRef gstate = context->gstate; ArtVpath *final, *transformed, *vpath; ArtRender *render; int x0, x1, y0, y1; /* Set these to zero to make the compiler happy. */ double cx = 0, cy = 0; x0 = (int) context->origin.x; y0 = (int) context->origin.y; x1 = (int) context->origin.x + context->size.width; y1 = (int) context->origin.y + context->size.height; /* First of all we loop through all the points in the vpath and flip the Y coordinate since the y-axis increases upwards. We also expand all curves into the final vpath. */ final_n_max = 128; final = art_new (ArtVpath, final_n_max); /* End the path. */ art_vpath_add_point (&context->vpath, &context->vpath_n, &context->vpath_n_max, ART_END, 0, 0); for (i = 0, vpath = context->vpath; vpath[i].code != ART_END; i++) { /* We found a bezcurve - transform it into a normal vpath. */ if (vpath[i].code == ART_CURVETO) { ArtVpath *bezpath; ArtBpath bez [3]; bez[0].code = ART_MOVETO; bez[0].x3 = cx; bez[0].y3 = cy; bez[1] = context->bpath [bez_index++]; bez[1].y1 = context->size.height - bez[1].y1; bez[1].y2 = context->size.height - bez[1].y2; bez[1].y3 = context->size.height - bez[1].y3; bez[2].code = ART_END; bezpath = art_bez_path_to_vec (bez, 0.5); /* ?? from state. */ for (n = 1; bezpath[n].code != ART_END; n++) { art_vpath_add_point (&final, &final_n, &final_n_max, bezpath[n].code, bezpath[n].x, bezpath[n].y); } } else { art_vpath_add_point (&final, &final_n, &final_n_max, vpath[i].code, vpath[i].x, context->size.height - vpath[i].y); } cx = final [final_n - 1].x; cy = final [final_n - 1].y; } /* End final path. */ art_vpath_add_point (&final, &final_n, &final_n_max, ART_END, 0, 0); /* Transform the final path using the current CTM. */ transformed = art_vpath_affine_transform (final, gstate->ctm); art_free (final); /* If drawing mode is not kGFPathStroke we will perform some form of filling. */ if (mode != kGFPathStroke) { int wind_rule, rowstride, alpha_info, alpha_pos; ArtSVP *tmp_svp, *uncrossed, *svp; tmp_svp = art_svp_from_vpath (transformed); uncrossed = art_svp_uncross (tmp_svp); art_free (tmp_svp); wind_rule = ART_WIND_RULE_NONZERO; if (mode == kGFPathEOFill || mode == kGFPathEOFillStroke) wind_rule = ART_WIND_RULE_ODDEVEN; svp = art_svp_rewind_uncrossed (uncrossed, wind_rule); art_free (uncrossed); rowstride = context->size.width * (context->n_components + 1) * context->n_bits_per_component / 8; alpha_info = alpha_info_to_libart_alpha_info (context->alpha_info); alpha_pos = alpha_info_to_libart_alpha_pos (context->alpha_info); render = art_render_new (x0, y0, x1 - x0, y1 - y0, context->dst_buffer, rowstride, 3, 8, alpha_info, alpha_pos, NULL); art_render_svp (render, svp); (*gstate->stroke_space->render_fn) (gstate->fill_space, gstate->fill_color, render); art_render_mask_solid (render, MAX_OPACITY (gstate->alpha_value)); art_render_invoke (render); art_free (svp); } if (mode == kGFPathStroke || mode == kGFPathFillStroke || mode == kGFPathEOFillStroke) { int rowstride, alpha_info, alpha_pos; ArtSVP *tmp_svp, *svp; /* Stroke the path into a SVP. */ tmp_svp = art_svp_vpath_stroke (transformed, gstate->line_join, gstate->line_cap, gstate->line_width, gstate->miter_limit, gstate->flatness); svp = art_svp_uncross (tmp_svp); art_free (tmp_svp); rowstride = context->size.width * (context->n_components + 1) * context->n_bits_per_component / 8; alpha_info = alpha_info_to_libart_alpha_info (context->alpha_info); alpha_pos = alpha_info_to_libart_alpha_pos (context->alpha_info); render = art_render_new (x0, y0, x1 - x0, y1 - y0, context->dst_buffer, rowstride, 3, 8, alpha_info, alpha_pos, NULL); art_render_svp (render, svp); (*gstate->stroke_space->render_fn) (gstate->stroke_space, gstate->stroke_color, render); art_render_mask_solid (render, MAX_OPACITY (gstate->alpha_value)); art_render_invoke (render); art_free (svp); } art_free (transformed); } /* Convenience function for filling a path. */ void GFContextFillPath (GFContextRef context) { GFContextDrawPath (context, kGFPathFill); } /* Convenience function for filling a path with even-odd winding rule. */ void GFContextEOFillPath (GFContextRef context) { GFContextDrawPath (context, kGFPathEOFill); } /* Convenience function for stroking a path. */ void GFContextStrokePath (GFContextRef context) { GFContextDrawPath (context, kGFPathStroke); } /* Scale CTM according to SX and SY. */ void GFContextScaleCTM (GFContextRef context, float sx, float sy) { double ctm [6]; art_affine_scale (ctm, sx, sy); art_affine_multiply (context->gstate->ctm, ctm, context->gstate->ctm); } /* Scale CTM with TH radias. */ void GFContextRotateCTM (GFContextRef context, float th) { double ctm [6]; art_affine_rotate (ctm, th * (180.0/M_PI)); art_affine_multiply (context->gstate->ctm, ctm, context->gstate->ctm); } /* Translate CTM by TX and TY. */ void GFContextTranslateCTM (GFContextRef context, float tx, float ty) { double ctm [6]; art_affine_translate (ctm, tx, ty); art_affine_multiply (context->gstate->ctm, ctm, context->gstate->ctm); }