123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include "config.h"
- #include <stdlib.h>
- #include <string.h>
- #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
- #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
- #include <mach/mach_host.h>
- #include <sys/mman.h>
- #endif
- #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 40000
- #include <ImageIO/ImageIO.h>
- #endif
- #endif
- #include <gvc/gvplugin_device.h>
- #include <gvc/gvplugin_render.h>
- #include <cgraph/cgraph.h>
- #include "gvplugin_quartz.h"
- static CGFloat dashed[] = { 6.0 };
- static CGFloat dotted[] = { 2.0, 6.0 };
- static void quartzgen_begin_job(GVJ_t * job)
- {
- switch (job->device.id) {
- case FORMAT_CGIMAGE:
- /* save the passed-in context in the window field, so we can create a CGContext in the context field later on */
- job->window = job->context;
- *((CGImageRef *) job->window) = NULL;
- }
-
- job->context = NULL;
- }
- static void quartzgen_end_job(GVJ_t * job)
- {
- CGContextRef context = job->context;
-
- #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
- #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
- void* context_data;
- size_t context_datalen;
-
- switch (job->device.id) {
-
- case FORMAT_PDF:
- context_data = NULL;
- context_datalen = 0;
- break;
-
- default:
- context_data = CGBitmapContextGetData(context);
- context_datalen = CGBitmapContextGetBytesPerRow(context) * CGBitmapContextGetHeight(context);
- break;
- }
- #endif
- #endif
- switch (job->device.id) {
- case FORMAT_PDF:
- /* save the PDF */
- CGPDFContextClose(context);
- break;
- case FORMAT_CGIMAGE:
- /* create an image and save it where the window field is, which was set to the passed-in context at begin job */
- *((CGImageRef *) job->window) = CGBitmapContextCreateImage(context);
- break;
- #if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040) || \
- (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 40000)
- default: /* bitmap formats */
- {
- /* create an image destination */
- CGDataConsumerRef data_consumer =
- CGDataConsumerCreate(job,
- &device_data_consumer_callbacks);
- CGImageDestinationRef image_destination =
- CGImageDestinationCreateWithDataConsumer(data_consumer,
- format_to_uti((format_type)job->device.id), 1, NULL);
- /* add the bitmap image to the destination and save it */
- CGImageRef image = CGBitmapContextCreateImage(context);
- CGImageDestinationAddImage(image_destination, image, NULL);
- CGImageDestinationFinalize(image_destination);
- /* clean up */
- if (image_destination)
- CFRelease(image_destination);
- CGImageRelease(image);
- CGDataConsumerRelease(data_consumer);
- }
- break;
- #endif
- }
-
- CGContextRelease(context);
- #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
- #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
- if (context_data && context_datalen)
- munmap(context_data, context_datalen);
- #endif
- #endif
- }
- static void quartzgen_begin_page(GVJ_t * job)
- {
- CGRect bounds = CGRectMake(0.0, 0.0, job->width, job->height);
- if (!job->context) {
- switch (job->device.id) {
- case FORMAT_PDF:
- {
- /* create the auxiliary info for PDF content, author and title */
- CFStringRef auxiliaryKeys[] = {
- kCGPDFContextCreator,
- kCGPDFContextTitle
- };
- CFStringRef auxiliaryValues[] = {
- CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
- CFSTR("%s %s"),
- job->common->info[0],
- job->common->info[1]),
- job->obj->type ==
- ROOTGRAPH_OBJTYPE ?
- CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
- (const UInt8 *) agnameof(job->obj->u.g),
- strlen(agnameof(job->obj->u.g)),
- kCFStringEncodingUTF8,
- false,
- kCFAllocatorNull)
- : CFSTR("")
- };
- CFDictionaryRef auxiliaryInfo =
- CFDictionaryCreate(kCFAllocatorDefault,
- (const void **) &auxiliaryKeys,
- (const void **) &auxiliaryValues,
- sizeof(auxiliaryValues) /
- sizeof(auxiliaryValues[0]),
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- /* create a PDF for drawing into */
- CGDataConsumerRef data_consumer =
- CGDataConsumerCreate(job,
- &device_data_consumer_callbacks);
- job->context =
- CGPDFContextCreate(data_consumer, &bounds,
- auxiliaryInfo);
- /* clean up */
- CGDataConsumerRelease(data_consumer);
- CFRelease(auxiliaryInfo);
- for (size_t i = 0; i < sizeof(auxiliaryValues) / sizeof(auxiliaryValues[0]);
- ++i)
- CFRelease(auxiliaryValues[i]);
- }
- break;
- default: /* bitmap formats */
- {
- size_t bytes_per_row =
- (job->width * BYTES_PER_PIXEL +
- BYTE_ALIGN) & ~BYTE_ALIGN;
- void *buffer = NULL;
- #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
- #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
- /* iPhoneOS has no swap files for memory, so if we're short of memory we need to make our own temp scratch file to back it */
- size_t buffer_size = job->height * bytes_per_row;
- mach_msg_type_number_t vm_info_size = HOST_VM_INFO_COUNT;
- vm_statistics_data_t vm_info;
- if (host_statistics
- (mach_host_self(), HOST_VM_INFO,
- (host_info_t) & vm_info,
- &vm_info_size) != KERN_SUCCESS
- || buffer_size * 2 >
- vm_info.free_count * vm_page_size) {
- FILE *temp_file = tmpfile();
- if (temp_file) {
- int temp_file_descriptor = fileno(temp_file);
- if (temp_file_descriptor >= 0
- && ftruncate(temp_file_descriptor,
- buffer_size) == 0) {
- buffer = mmap(NULL, buffer_size, PROT_READ | PROT_WRITE,
- MAP_FILE | MAP_PRIVATE, temp_file_descriptor, 0);
- if (buffer == MAP_FAILED)
- buffer = NULL;
- }
- fclose(temp_file);
- }
- }
- if (buffer == NULL) {
- buffer = mmap(NULL, buffer_size, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_PRIVATE, -1, 0);
- if (buffer == MAP_FAILED) {
- buffer = NULL;
- }
- }
- #endif
- #endif
- /* create a true color bitmap for drawing into */
- CGColorSpaceRef color_space =
- CGColorSpaceCreateDeviceRGB();
- job->context = CGBitmapContextCreate(buffer, /* data: MacOSX lets system allocate, iPhoneOS use manual memory mapping */
- job->width, /* width in pixels */
- job->height, /* height in pixels */
- BITS_PER_COMPONENT, /* bits per component */
- bytes_per_row, /* bytes per row: align to 16 byte boundary */
- color_space, /* color space: device RGB */
- kCGImageAlphaPremultipliedFirst /* bitmap info: premul ARGB has best support in OS X */
- );
- job->imagedata = CGBitmapContextGetData(job->context);
- /* clean up */
- CGColorSpaceRelease(color_space);
- }
- break;
- }
- }
- /* start the page (if this is a paged context) and graphics state */
- CGContextRef context = job->context;
- CGContextBeginPage(context, &bounds);
- CGContextSaveGState(context);
- /* CGContextSetMiterLimit(context, 1.0); */
- /* CGContextSetLineJoin(context, kCGLineJoinBevel); */
- /* set up the context transformation */
- CGContextScaleCTM(context, job->scale.x, job->scale.y);
- CGContextRotateCTM(context, job->rotation * M_PI / 180.0);
- CGContextTranslateCTM(context, job->translation.x, job->translation.y);
- }
- static void quartzgen_end_page(GVJ_t * job)
- {
- /* end the page (if this is a paged context) and graphics state */
- CGContextRef context = job->context;
- CGContextRestoreGState(context);
- CGContextEndPage(context);
- }
- static void quartzgen_begin_anchor(GVJ_t * job, char *url, char *tooltip,
- char *target, char *id)
- {
- (void)tooltip;
- (void)target;
- (void)id;
- pointf *url_map = job->obj->url_map_p;
- if (url && url_map) {
- /* set up the hyperlink to the given url */
- CGContextRef context = job->context;
- CFURLRef uri =
- CFURLCreateWithBytes(kCFAllocatorDefault, (const UInt8 *) url,
- strlen(url), kCFStringEncodingUTF8, NULL);
- CGPDFContextSetURLForRect(context, uri,
- /* need to reverse the CTM on the area to get it to work */
- CGRectApplyAffineTransform(CGRectMake
- (url_map[0].x,
- url_map[0].y,
- url_map[1].
- x -
- url_map[0].x,
- url_map[1].
- y -
- url_map[0].
- y),
- CGContextGetCTM
- (context))
- );
- /* clean up */
- CFRelease(uri);
- }
- }
- static void quartzgen_path(GVJ_t * job, int filled)
- {
- CGContextRef context = job->context;
- /* set up colors */
- if (filled)
- CGContextSetRGBFillColor(context, job->obj->fillcolor.u.RGBA[0],
- job->obj->fillcolor.u.RGBA[1],
- job->obj->fillcolor.u.RGBA[2],
- job->obj->fillcolor.u.RGBA[3]);
- CGContextSetRGBStrokeColor(context, job->obj->pencolor.u.RGBA[0],
- job->obj->pencolor.u.RGBA[1],
- job->obj->pencolor.u.RGBA[2],
- job->obj->pencolor.u.RGBA[3]);
- /* set up line style */
- const CGFloat *segments;
- size_t segment_count;
- switch (job->obj->pen) {
- case PEN_DASHED:
- segments = dashed;
- segment_count = sizeof(dashed) / sizeof(CGFloat);
- break;
- case PEN_DOTTED:
- segments = dotted;
- segment_count = sizeof(dotted) / sizeof(CGFloat);
- break;
- default:
- segments = NULL;
- segment_count = 0;
- break;
- }
- CGContextSetLineDash(context, 0.0, segments, segment_count);
- /* set up line width */
- CGContextSetLineWidth(context, job->obj->penwidth); // *job->scale.x);
- /* draw the path */
- CGContextDrawPath(context, filled ? kCGPathFillStroke : kCGPathStroke);
- }
- static void quartzgen_textspan(GVJ_t * job, pointf p, textspan_t * span)
- {
- CGContextRef context = job->context;
- /* adjust text position */
- switch (span->just) {
- case 'r':
- p.x -= span->size.x;
- break;
- case 'l':
- p.x -= 0.0;
- break;
- case 'n':
- default:
- p.x -= span->size.x / 2.0;
- break;
- }
- p.y += span->yoffset_centerline;
- void *layout;
- if (span->free_layout == &quartz_free_layout)
- layout = span->layout;
- else
- layout =
- quartz_new_layout(span->font->name, span->font->size, span->str);
- CGContextSetRGBFillColor(context, job->obj->pencolor.u.RGBA[0],
- job->obj->pencolor.u.RGBA[1],
- job->obj->pencolor.u.RGBA[2],
- job->obj->pencolor.u.RGBA[3]);
- quartz_draw_layout(layout, context, CGPointMake(p.x, p.y));
- if (span->free_layout != &quartz_free_layout)
- quartz_free_layout(layout);
- }
- static void quartzgen_ellipse(GVJ_t * job, pointf * A, int filled)
- {
- /* convert ellipse into the current path */
- CGContextRef context = job->context;
- double dx = A[1].x - A[0].x;
- double dy = A[1].y - A[0].y;
- CGContextAddEllipseInRect(context,
- CGRectMake(A[0].x - dx, A[0].y - dy,
- dx * 2.0, dy * 2.0));
- /* draw the ellipse */
- quartzgen_path(job, filled);
- }
- static void quartzgen_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
- /* convert polygon into the current path */
- CGContextRef context = job->context;
- CGContextMoveToPoint(context, A[0].x, A[0].y);
- for (size_t i = 1; i < n; ++i)
- CGContextAddLineToPoint(context, A[i].x, A[i].y);
- CGContextClosePath(context);
- /* draw the ellipse */
- quartzgen_path(job, filled);
- }
- static void quartzgen_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
- /* convert bezier into the current path */
- CGContextRef context = job->context;
- CGContextMoveToPoint(context, A[0].x, A[0].y);
- for (size_t i = 1; i < n; i += 3)
- CGContextAddCurveToPoint(context, A[i].x, A[i].y, A[i + 1].x,
- A[i + 1].y, A[i + 2].x, A[i + 2].y);
- /* draw the ellipse */
- quartzgen_path(job, filled);
- }
- static void quartzgen_polyline(GVJ_t *job, pointf *A, size_t n) {
- /* convert polyline into the current path */
- CGContextRef context = job->context;
- CGContextMoveToPoint(context, A[0].x, A[0].y);
- for (size_t i = 1; i < n; ++i)
- CGContextAddLineToPoint(context, A[i].x, A[i].y);
- /* draw the ellipse */
- quartzgen_path(job, 0);
- }
- static gvrender_engine_t quartzgen_engine = {
- quartzgen_begin_job,
- quartzgen_end_job,
- 0, /* quartzgen_begin_graph */
- 0, /* quartzgen_end_graph */
- 0, /* quartzgen_begin_layer */
- 0, /* quartzgen_end_layer */
- quartzgen_begin_page,
- quartzgen_end_page,
- 0, /* quartzgen_begin_cluster */
- 0, /* quartzgen_end_cluster */
- 0, /* quartzgen_begin_nodes */
- 0, /* quartzgen_end_nodes */
- 0, /* quartzgen_begin_edges */
- 0, /* quartzgen_end_edges */
- 0, /* quartzgen_begin_node */
- 0, /* quartzgen_end_node */
- 0, /* quartzgen_begin_edge */
- 0, /* quartzgen_end_edge */
- quartzgen_begin_anchor,
- 0, /* quartzgen_end_anchor */
- 0, /* quartzgen_begin_label */
- 0, /* quartzgen_end_label */
- quartzgen_textspan,
- 0,
- quartzgen_ellipse,
- quartzgen_polygon,
- quartzgen_bezier,
- quartzgen_polyline,
- 0, /* quartzgen_comment */
- 0, /* quartzgen_library_shape */
- };
- static gvrender_features_t render_features_quartz = {
- GVRENDER_DOES_MAPS | GVRENDER_DOES_MAP_RECTANGLE | GVRENDER_DOES_TRANSFORM, /* flags */
- 4., /* default pad - graph units */
- NULL, /* knowncolors */
- 0, /* sizeof knowncolors */
- RGBA_DOUBLE /* color_type */
- };
- #if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040) || \
- (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000)
- static gvdevice_features_t device_features_quartz = {
- GVDEVICE_BINARY_FORMAT | GVDEVICE_DOES_TRUECOLOR, /* flags */
- {0., 0.}, /* default margin - points */
- {0., 0.}, /* default page width, height - points */
- {72., 72.} /* dpi */
- };
- #endif
- static gvdevice_features_t device_features_quartz_paged = {
- GVDEVICE_DOES_PAGES | GVDEVICE_BINARY_FORMAT | GVDEVICE_DOES_TRUECOLOR | GVRENDER_NO_WHITE_BG, /* flags */
- {36., 36.}, /* default margin - points */
- {0., 0.}, /* default page width, height - points */
- {72., 72.} /* dpi */
- };
- gvplugin_installed_t gvrender_quartz_types[] = {
- {0, "quartz", 1, &quartzgen_engine, &render_features_quartz},
- {0, NULL, 0, NULL, NULL}
- };
- gvplugin_installed_t gvdevice_quartz_types[] = {
- {FORMAT_PDF, "pdf:quartz", 8, NULL, &device_features_quartz_paged},
- #if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040) || \
- (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000)
- {FORMAT_CGIMAGE, "cgimage:quartz", 8, NULL, &device_features_quartz},
- #endif
- #if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040) || \
- (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 40000)
- {FORMAT_BMP, "bmp:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_GIF, "gif:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_ICO, "ico:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_JPEG, "jpe:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_JPEG, "jpeg:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_JPEG, "jpg:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_JPEG2000, "jp2:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_PNG, "png:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_TIFF, "tif:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_TIFF, "tiff:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_TGA, "tga:quartz", 8, NULL, &device_features_quartz},
- #endif
- #ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
- #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
- {FORMAT_EXR, "exr:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_ICNS, "icns:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_PICT, "pct:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_PICT, "pict:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_PSD, "psd:quartz", 8, NULL, &device_features_quartz},
- {FORMAT_SGI, "sgi:quartz", 8, NULL, &device_features_quartz},
- #endif
- #endif
- {0, NULL, 0, NULL, NULL}
- };
|