<commit_before>/* Copyright 2012 <NAME>.
   This file is part of SGLib.  SGLib is licensed under the terms of the
   2-clause BSD license.  For more information, see LICENSE.txt. */
#include "sg/file.h"
#include "sg/pixbuf.h"
#include <ApplicationServices/ApplicationServices.h>
#include <assert.h>

static void releaseData(void *info, const void *data, size_t size)
{
    (void) info;
    (void) data;
    (void) size;
}

static CGDataProviderRef
getDataProvider(const void *data, size_t length)
{
    return CGDataProviderCreateWithData(NULL, data, length, releaseData);
}

static int
imageToPixbuf(struct sg_pixbuf *pbuf, CGImageRef img, struct sg_error **err)
{
    // FIXME: We assume here that the image is color.
    size_t w = CGImageGetWidth(img), h = CGImageGetHeight(img), pw, ph;
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(img);
    sg_pixbuf_format_t pfmt;
    int r, alpha;

    switch (alphaInfo) {
    case kCGImageAlphaNone:
    case kCGImageAlphaNoneSkipLast:
    case kCGImageAlphaNoneSkipFirst:
        alpha = 0;
        break;

    case kCGImageAlphaPremultipliedLast:
    case kCGImageAlphaPremultipliedFirst:
    case kCGImageAlphaLast:
    case kCGImageAlphaFirst:
    default:
        alpha = 1;
        break;
    }
    pfmt = alpha ? SG_RGBA : SG_RGBX;

    r = sg_pixbuf_set(pbuf, pfmt, w, h, err);
    if (r)
        goto error;
    pw = pbuf->pwidth;
    ph = pbuf->pheight;
    r = sg_pixbuf_alloc(pbuf, err);
    if (r)
        goto error;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    assert(colorSpace);
    CGBitmapInfo info = alpha
        ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast;
    CGContextRef cxt = CGBitmapContextCreate(
        pbuf->data, pw, ph, 8, pbuf->rowbytes, colorSpace, info);
    assert(cxt);
    CGColorSpaceRelease(colorSpace);

    CGContextSetBlendMode(cxt, kCGBlendModeCopy);
    CGContextSetRGBFillColor(cxt, 0.0f, 0.0f, 0.0f, 0.0f);
    if (w < pw)
        CGContextFillRect(cxt, CGRectMake(w, 0, pw - w, h));
    if (h < ph)
        CGContextFillRect(cxt, CGRectMake(0, h, pw, pw - h));
    CGContextDrawImage(cxt, CGRectMake(0, 0, w, h), img);

    CGImageRelease(img);
    CGContextRelease(cxt);
    return 0;

error:
    return -1;
}

int
sg_pixbuf_loadpng(struct sg_pixbuf *pbuf, const void *data, size_t length,
                  struct sg_error **err)
{
    CGDataProviderRef dp = getDataProvider(data, length);
    assert(dp);
    CGImageRef img = CGImageCreateWithPNGDataProvider(
        dp, NULL, false, kCGRenderingIntentDefault);
    CGDataProviderRelease(dp);
    assert(img);
    return imageToPixbuf(pbuf, img, err);
}

int
sg_pixbuf_loadjpeg(struct sg_pixbuf *pbuf, const void *data, size_t length,
                   struct sg_error **err)
{
    CGDataProviderRef dp = getDataProvider(data, length);
    assert(dp);
    CGImageRef img = CGImageCreateWithJPEGDataProvider(
        dp, NULL, false, kCGRenderingIntentDefault);
    CGDataProviderRelease(dp);
    assert(img);
    return imageToPixbuf(pbuf, img, err);
}

static size_t
sgFilePutBytes(void *info, const void *buffer, size_t count)
{
    struct sg_file *fp = info;
    int r = fp->write(fp, buffer, count);
    return r < 0 ? 0 : (size_t) r;
}

static void
sgFileRelease(void *info)
{
    (void) info;
}

static CGDataConsumerRef
getDataConsumer(struct sg_file *fp)
{
    CGDataConsumerCallbacks cb;
    cb.putBytes = sgFilePutBytes;
    cb.releaseConsumer = sgFileRelease;
    CGDataConsumerRef dc = CGDataConsumerCreate(fp, &cb);
    assert(dc != NULL);
    return dc;
}

int
sg_pixbuf_writepng(struct sg_pixbuf *pbuf, struct sg_file *fp,
                   struct sg_error **err)
{
    int nchan;
    CGBitmapInfo ifo;
    switch (pbuf->format) {
        case SG_Y:    nchan = 1; ifo = kCGImageAlphaNone; break;
        case SG_YA:   nchan = 2; ifo = kCGImageAlphaLast; break;
        /* SG_RGB not supported */
        case SG_RGBA: nchan = 4; ifo = kCGImageAlphaNoneSkipLast; break;
        /* case SG_RGBA: nchan = 4; ifo = kCGImageAlphaPremultipliedLast;
           break; */
        default: assert(0);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    assert(colorSpace != NULL);

    CGDataProviderRef dp = getDataProvider(
        pbuf->data, (size_t) pbuf->rowbytes * pbuf->iheight);
    assert(dp != NULL);

    CGImageRef img = CGImageCreate(
        pbuf->iwidth, pbuf->iheight,
        8, nchan * 8, pbuf->rowbytes,
        colorSpace, ifo, dp, NULL, false, kCGRenderingIntentDefault);
    assert(img != NULL);

    CGDataConsumerRef dc = getDataConsumer(fp);

    CGImageDestinationRef dest = CGImageDestinationCreateWithDataConsumer(
        dc, kUTTypePNG, 1, NULL);
    assert(dest != NULL);

    CGImageDestinationAddImage(dest, img, NULL);
    bool br = CGImageDestinationFinalize(dest);
    assert(br);

    CFRelease(dest);
    CFRelease(dc);
    CFRelease(img);
    CFRelease(dp);
    CFRelease(colorSpace);

    return 0;
    (void) err;
}
<commit_msg>osx: fix NPOT texture loading<commit_after>/* Copyright 2012 <NAME>.
   This file is part of SGLib.  SGLib is licensed under the terms of the
   2-clause BSD license.  For more information, see LICENSE.txt. */
#include "sg/file.h"
#include "sg/pixbuf.h"
#include <ApplicationServices/ApplicationServices.h>
#include <assert.h>

static void releaseData(void *info, const void *data, size_t size)
{
    (void) info;
    (void) data;
    (void) size;
}

static CGDataProviderRef
getDataProvider(const void *data, size_t length)
{
    return CGDataProviderCreateWithData(NULL, data, length, releaseData);
}

static int
imageToPixbuf(struct sg_pixbuf *pbuf, CGImageRef img, struct sg_error **err)
{
    // FIXME: We assume here that the image is color.
    size_t w = CGImageGetWidth(img), h = CGImageGetHeight(img), pw, ph;
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(img);
    sg_pixbuf_format_t pfmt;
    int r, alpha;

    switch (alphaInfo) {
    case kCGImageAlphaNone:
    case kCGImageAlphaNoneSkipLast:
    case kCGImageAlphaNoneSkipFirst:
        alpha = 0;
        break;

    case kCGImageAlphaPremultipliedLast:
    case kCGImageAlphaPremultipliedFirst:
    case kCGImageAlphaLast:
    case kCGImageAlphaFirst:
    default:
        alpha = 1;
        break;
    }
    pfmt = alpha ? SG_RGBA : SG_RGBX;

    r = sg_pixbuf_set(pbuf, pfmt, w, h, err);
    if (r)
        goto error;
    pw = pbuf->pwidth;
    ph = pbuf->pheight;
    r = sg_pixbuf_alloc(pbuf, err);
    if (r)
        goto error;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    assert(colorSpace);
    CGBitmapInfo info = alpha
        ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast;
    CGContextRef cxt = CGBitmapContextCreate(
        pbuf->data, pw, ph, 8, pbuf->rowbytes, colorSpace, info);
    assert(cxt);
    CGColorSpaceRelease(colorSpace);

    CGContextSetBlendMode(cxt, kCGBlendModeCopy);
    CGContextSetRGBFillColor(cxt, 0.0f, 0.0f, 0.0f, 0.0f);
    if (w < pw)
        CGContextFillRect(cxt, CGRectMake(w, 0, pw - w, h));
    if (h < ph)
        CGContextFillRect(cxt, CGRectMake(0, 0, pw, pw - h));
    CGContextDrawImage(cxt, CGRectMake(0, ph - h, w, h), img);

    CGImageRelease(img);
    CGContextRelease(cxt);
    return 0;

error:
    return -1;
}

int
sg_pixbuf_loadpng(struct sg_pixbuf *pbuf, const void *data, size_t length,
                  struct sg_error **err)
{
    CGDataProviderRef dp = getDataProvider(data, length);
    assert(dp);
    CGImageRef img = CGImageCreateWithPNGDataProvider(
        dp, NULL, false, kCGRenderingIntentDefault);
    CGDataProviderRelease(dp);
    assert(img);
    return imageToPixbuf(pbuf, img, err);
}

int
sg_pixbuf_loadjpeg(struct sg_pixbuf *pbuf, const void *data, size_t length,
                   struct sg_error **err)
{
    CGDataProviderRef dp = getDataProvider(data, length);
    assert(dp);
    CGImageRef img = CGImageCreateWithJPEGDataProvider(
        dp, NULL, false, kCGRenderingIntentDefault);
    CGDataProviderRelease(dp);
    assert(img);
    return imageToPixbuf(pbuf, img, err);
}

static size_t
sgFilePutBytes(void *info, const void *buffer, size_t count)
{
    struct sg_file *fp = info;
    int r = fp->write(fp, buffer, count);
    return r < 0 ? 0 : (size_t) r;
}

static void
sgFileRelease(void *info)
{
    (void) info;
}

static CGDataConsumerRef
getDataConsumer(struct sg_file *fp)
{
    CGDataConsumerCallbacks cb;
    cb.putBytes = sgFilePutBytes;
    cb.releaseConsumer = sgFileRelease;
    CGDataConsumerRef dc = CGDataConsumerCreate(fp, &cb);
    assert(dc != NULL);
    return dc;
}

int
sg_pixbuf_writepng(struct sg_pixbuf *pbuf, struct sg_file *fp,
                   struct sg_error **err)
{
    int nchan;
    CGBitmapInfo ifo;
    switch (pbuf->format) {
        case SG_Y:    nchan = 1; ifo = kCGImageAlphaNone; break;
        case SG_YA:   nchan = 2; ifo = kCGImageAlphaLast; break;
        /* SG_RGB not supported */
        case SG_RGBA: nchan = 4; ifo = kCGImageAlphaNoneSkipLast; break;
        /* case SG_RGBA: nchan = 4; ifo = kCGImageAlphaPremultipliedLast;
           break; */
        default: assert(0);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    assert(colorSpace != NULL);

    CGDataProviderRef dp = getDataProvider(
        pbuf->data, (size_t) pbuf->rowbytes * pbuf->iheight);
    assert(dp != NULL);

    CGImageRef img = CGImageCreate(
        pbuf->iwidth, pbuf->iheight,
        8, nchan * 8, pbuf->rowbytes,
        colorSpace, ifo, dp, NULL, false, kCGRenderingIntentDefault);
    assert(img != NULL);

    CGDataConsumerRef dc = getDataConsumer(fp);

    CGImageDestinationRef dest = CGImageDestinationCreateWithDataConsumer(
        dc, kUTTypePNG, 1, NULL);
    assert(dest != NULL);

    CGImageDestinationAddImage(dest, img, NULL);
    bool br = CGImageDestinationFinalize(dest);
    assert(br);

    CFRelease(dest);
    CFRelease(dc);
    CFRelease(img);
    CFRelease(dp);
    CFRelease(colorSpace);

    return 0;
    (void) err;
}

{'fraction_non_alphanumeric': 0.10426445620693099, 'fraction_numerical': 0.006591431139518626, 'mean_word_length': 3.246819338422392, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '631938', 'n_tokens_mistral': 3657, 'n_tokens_neox': 3415, 'n_words': 843}