/* * Copyright 2011 Sven Weidauer * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf 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; version 2 of the License. * * NetSurf 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, see . */ /** * \file * Cocoa implementation of bitmap operations. */ #import #import "netsurf/browser_window.h" #import "netsurf/plotters.h" #import "netsurf/bitmap.h" #import "netsurf/content.h" #import "cocoa/plotter.h" #import "cocoa/bitmap.h" #import "cocoa/arc.h" #define BITS_PER_SAMPLE (8) #define SAMPLES_PER_PIXEL (4) #define BITS_PER_PIXEL (BITS_PER_SAMPLE * SAMPLES_PER_PIXEL) #define BYTES_PER_PIXEL (BITS_PER_PIXEL / 8) #define RED_OFFSET (0) #define GREEN_OFFSET (1) #define BLUE_OFFSET (2) #define ALPHA_OFFSET (3) static CGImageRef cocoa_prepare_bitmap(void *bitmap); static inline NSMapTable *cocoa_get_bitmap_cache(void) { static NSMapTable *cache = nil; if (cache == nil) { cache = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0); } return cache; } static inline NSBitmapImageRep *get_bitmap(void *bitmap) { NSCParameterAssert(bitmap); return (__bridge NSBitmapImageRep *)bitmap; } static int bitmap_get_width(void *bitmap) { return (int)get_bitmap(bitmap).pixelsWide; } static int bitmap_get_height(void *bitmap) { return (int)get_bitmap(bitmap).pixelsHigh; } static bool bitmap_get_opaque(void *bitmap) { return get_bitmap(bitmap).isOpaque; } static void bitmap_destroy(void *bitmap) { NSCParameterAssert(NULL != bitmap); cocoa_bitmap_modified(bitmap); arc_release(bitmap); } static void *bitmap_create(int width, int height, unsigned int state) { NSBitmapImageRep *bmp = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height bitsPerSample:BITS_PER_SAMPLE samplesPerPixel:SAMPLES_PER_PIXEL hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bitmapFormat:NSAlphaNonpremultipliedBitmapFormat bytesPerRow:BYTES_PER_PIXEL * width bitsPerPixel:BITS_PER_PIXEL]; return arc_retain(bmp); } static void bitmap_set_opaque(void *bitmap, bool opaque) { get_bitmap(bitmap).opaque = opaque ? YES : NO; } static unsigned char *bitmap_get_buffer(void *bitmap) { return get_bitmap(bitmap).bitmapData; } static size_t bitmap_get_rowstride(void *bitmap) { return get_bitmap(bitmap).bytesPerRow; } static size_t bitmap_get_bpp(void *bitmap) { return get_bitmap(bitmap).bitsPerPixel / 8; } static bool bitmap_test_opaque(void *bitmap) { NSCParameterAssert(bitmap_get_bpp(bitmap) == BYTES_PER_PIXEL); unsigned char *buf = bitmap_get_buffer(bitmap); const size_t height = bitmap_get_height(bitmap); const size_t width = bitmap_get_width(bitmap); const size_t line_step = bitmap_get_rowstride(bitmap) - BYTES_PER_PIXEL * width; for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < height; x++) { if (buf[ALPHA_OFFSET] != 0xFF) return false; buf += BYTES_PER_PIXEL; } buf += line_step; } return true; } static bool bitmap_save(void *bitmap, const char *path, unsigned flags) { NSData *tiff = get_bitmap(bitmap).TIFFRepresentation; return [tiff writeToFile:@(path) atomically:YES]; } void cocoa_bitmap_modified(void *bitmap) { NSMapTable *cache = cocoa_get_bitmap_cache(); CGImageRef image = NSMapGet(cache, bitmap); if (NULL != image) { CGImageRelease(image); NSMapRemove(cache, bitmap); } } CGImageRef cocoa_get_cgimage(void *bitmap) { NSMapTable *cache = cocoa_get_bitmap_cache(); CGImageRef result = NSMapGet(cache, bitmap); if (NULL == result) { result = cocoa_prepare_bitmap(bitmap); NSMapInsertKnownAbsent(cache, bitmap, result); } return result; } static CGImageRef cocoa_prepare_bitmap(void *bitmap) { NSBitmapImageRep *bmp = get_bitmap(bitmap); size_t w = bmp.pixelsWide; size_t h = bmp.pixelsHigh; CGImageRef original = bmp.CGImage; if (h <= 1) { return CGImageRetain(original); } void *data = malloc(4 * w * h); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(data, w, h, BITS_PER_SAMPLE, BYTES_PER_PIXEL * w, colorSpace, [bmp isOpaque] ? kCGImageAlphaNoneSkipLast : kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); CGContextTranslateCTM(context, 0.0, h); CGContextScaleCTM(context, 1.0, -1.0); CGRect rect = CGRectMake(0, 0, w, h); CGContextClearRect(context, rect); CGContextDrawImage(context, rect, original); CGImageRef result = CGBitmapContextCreateImage(context); CGContextRelease(context); free(data); return result; } static nserror bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content) { int bwidth = bitmap_get_width(bitmap); int bheight = bitmap_get_height(bitmap); struct redraw_context ctx = { .interactive = false, .background_images = true, .plot = &cocoa_plotters }; CGColorSpaceRef cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); CGContextRef bitmapContext = CGBitmapContextCreate(bitmap_get_buffer(bitmap), bwidth, bheight, bitmap_get_bpp(bitmap) * 8 / 4, bitmap_get_rowstride(bitmap), cspace, kCGImageAlphaNoneSkipLast); CGColorSpaceRelease(cspace); int width = MIN(content_get_width(content), 1024); int height = ((width * bheight) + bwidth / 2) / bwidth; CGContextTranslateCTM(bitmapContext, 0, bheight); CGContextScaleCTM(bitmapContext, (CGFloat)bwidth / width, -(CGFloat)bheight / height); [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:bitmapContext flipped:YES]]; content_scaled_redraw(content, width, height, &ctx); [NSGraphicsContext setCurrentContext:nil]; CGContextRelease(bitmapContext); cocoa_bitmap_modified(bitmap); return NSERROR_OK; } static struct gui_bitmap_table bitmap_table = { .create = bitmap_create, .destroy = bitmap_destroy, .set_opaque = bitmap_set_opaque, .get_opaque = bitmap_get_opaque, .test_opaque = bitmap_test_opaque, .get_buffer = bitmap_get_buffer, .get_rowstride = bitmap_get_rowstride, .get_width = bitmap_get_width, .get_height = bitmap_get_height, .get_bpp = bitmap_get_bpp, .save = bitmap_save, .modified = cocoa_bitmap_modified, .render = bitmap_render, }; struct gui_bitmap_table *cocoa_bitmap_table = &bitmap_table;