mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-16 10:20:50 +00:00
This commit is contained in:
parent
6bef687401
commit
3a8596d02b
@ -1,12 +1,14 @@
|
||||
# Desktop Integration
|
||||
# Copyright (C) 2022 Igara Studio S.A.
|
||||
# Copyright (C) 2017-2018 David Capello
|
||||
|
||||
# Windows
|
||||
if(WIN32)
|
||||
# Thumbnails for Windows File Explorer
|
||||
add_subdirectory(win)
|
||||
endif()
|
||||
|
||||
# Linux-like
|
||||
if(UNIX AND NOT APPLE)
|
||||
elseif(APPLE)
|
||||
# QuickLook preview / macOS Finder
|
||||
add_subdirectory(osx)
|
||||
else(UNIX)
|
||||
# Linux-like
|
||||
add_subdirectory(linux)
|
||||
endif()
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (C) 2021 Igara Studio S.A.
|
||||
Copyright (C) 2021-2022 Igara Studio S.A.
|
||||
Copyright (C) 2017-2018 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
@ -22,3 +22,28 @@ create registry keys to associate your file type extension with
|
||||
your thumbnail handler.
|
||||
|
||||
More information [in the MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/cc144118.aspx).
|
||||
|
||||
## macOS
|
||||
|
||||
On macOS we have to create a QuickLook plugin/extension to display
|
||||
thumbnails and previews. The plugin is a `.qlgenerator` bundle which
|
||||
can be installed in the `~/Library/QuickLook`,
|
||||
`/System/Library/QuickLook`, or `/Library/QuickLook` directories, or
|
||||
included the in the same app bundle.
|
||||
|
||||
[macOS pre-10.15](https://developer.apple.com/documentation/quicklook/previews_or_thumbnail_images_for_macos_10_14_or_earlier?language=objc),
|
||||
has a COM-like functionality to load QuickLook plugins: the QuickLook
|
||||
daemon will use the information in our bundle
|
||||
[Info.plist](osx/Info.plist) to know which function to load and call
|
||||
from our library to create the plugin object. (The function generally
|
||||
is called `QuickLookGeneratorPluginFactory` but can has any name, the
|
||||
name is specified in a child element of `CFPlugInFactories` inside the
|
||||
`Info.plist` file.) Then the created object is used abstractly as a
|
||||
`IUnknown`, and the `QLGeneratorInterface` interface is queried to
|
||||
generate then thumbnails and previews.
|
||||
|
||||
We can test our `.qlgenerator` without installing it using the
|
||||
[`qlmanage` utility](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/Quicklook_Programming_Guide/Articles/QLDebugTest.html).
|
||||
|
||||
We target to macOS 10.9, but [we should migrate](https://developer.apple.com/videos/play/wwdc2019/719?time=944)
|
||||
to the new macOS 10.15 API in a near future (or provide both).
|
||||
|
10
src/desktop/osx/AsepriteThumbnailer.entitlements
Normal file
10
src/desktop/osx/AsepriteThumbnailer.entitlements
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
37
src/desktop/osx/CMakeLists.txt
Normal file
37
src/desktop/osx/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
# Desktop Integration
|
||||
# Copyright (c) 2022 Igara Studio S.A.
|
||||
|
||||
find_library(QUARTZ_LIBRARY Quartz)
|
||||
|
||||
add_library(AsepriteThumbnailer SHARED
|
||||
main.mm
|
||||
thumbnail.mm)
|
||||
|
||||
target_link_libraries(AsepriteThumbnailer
|
||||
laf-base
|
||||
dio-lib
|
||||
render-lib
|
||||
${QUARTZ_LIBRARY})
|
||||
|
||||
set_target_properties(AsepriteThumbnailer PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator/Contents
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
lib/AsepriteThumbnailer.framework/Versions/A/AsepriteThumbnailer
|
||||
lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
lib/AsepriteThumbnailer.framework/Versions/A/Resources/Info.plist
|
||||
lib/AsepriteThumbnailer.qlgenerator/Contents
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS/AsepriteThumbnailer
|
||||
${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator/Contents/Info.plist
|
||||
DEPENDS AsepriteThumbnailer)
|
||||
|
||||
add_custom_target(AsepriteThumbnailer.qlgenerator
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator)
|
58
src/desktop/osx/Info.plist
Normal file
58
src/desktop/osx/Info.plist
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>QLGenerator</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>dyn.ah62d4rv4ge80c65f</string>
|
||||
<string>dyn.ah62d4rv4ge80c65fsb3gw7df</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.aseprite.AsepriteThumbnailer</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CFPlugInFactories</key>
|
||||
<dict>
|
||||
<key>A5E9417E-6E7A-4B2D-85A4-84E114D7A960</key>
|
||||
<string>QuickLookGeneratorPluginFactory</string>
|
||||
</dict>
|
||||
<key>CFPlugInTypes</key>
|
||||
<dict>
|
||||
<key>5E2D9680-5022-40FA-B806-43349622E5B9</key>
|
||||
<array>
|
||||
<string>A5E9417E-6E7A-4B2D-85A4-84E114D7A960</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>CFPlugInUnloadFunction</key>
|
||||
<string></string>
|
||||
<key>QLThumbnailMinimumSize</key>
|
||||
<real>32</real>
|
||||
<key>QLPreviewWidth</key>
|
||||
<real>256</real>
|
||||
<key>QLPreviewHeight</key>
|
||||
<real>256</real>
|
||||
<key>QLNeedsToBeRunInMainThread</key>
|
||||
<true/>
|
||||
<key>QLSupportsConcurrentRequests</key>
|
||||
<false/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © Igara Studio S.A. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
209
src/desktop/osx/main.mm
Normal file
209
src/desktop/osx/main.mm
Normal file
@ -0,0 +1,209 @@
|
||||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include <CoreFoundation/CFPlugInCOM.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <QuickLook/QuickLook.h>
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "thumbnail.h"
|
||||
|
||||
// Just as a side note: We're using the same UUID as the Windows
|
||||
// Aseprite thumbnailer.
|
||||
//
|
||||
// If you're going to use this code, remember to change this UUID and
|
||||
// change it in the Info.plist file.
|
||||
#define PLUGIN_ID "A5E9417E-6E7A-4B2D-85A4-84E114D7A960"
|
||||
|
||||
static HRESULT Plugin_QueryInterface(void*, REFIID, LPVOID*);
|
||||
static ULONG Plugin_AddRef(void*);
|
||||
static ULONG Plugin_Release(void*);
|
||||
static OSStatus Plugin_GenerateThumbnailForURL(void*, QLThumbnailRequestRef, CFURLRef, CFStringRef, CFDictionaryRef, CGSize);
|
||||
static void Plugin_CancelThumbnailGeneration(void*, QLThumbnailRequestRef);
|
||||
static OSStatus Plugin_GeneratePreviewForURL(void*, QLPreviewRequestRef, CFURLRef, CFStringRef, CFDictionaryRef);
|
||||
static void Plugin_CancelPreviewGeneration(void*, QLPreviewRequestRef);
|
||||
|
||||
static QLGeneratorInterfaceStruct Plugin_vtbl = { // kQLGeneratorTypeID interface
|
||||
// IUnknown
|
||||
nullptr, // void* reserved
|
||||
Plugin_QueryInterface,
|
||||
Plugin_AddRef,
|
||||
Plugin_Release,
|
||||
// QLGeneratorInterface
|
||||
Plugin_GenerateThumbnailForURL,
|
||||
Plugin_CancelThumbnailGeneration,
|
||||
Plugin_GeneratePreviewForURL,
|
||||
Plugin_CancelPreviewGeneration
|
||||
};
|
||||
|
||||
// TODO it would be nice to create a C++ smart pointer/wrapper for CFUUIDRef type
|
||||
|
||||
struct Plugin {
|
||||
QLGeneratorInterfaceStruct* interface; // Must be a pointer
|
||||
CFUUIDRef factoryID;
|
||||
ULONG refCount = 1; // Starts with one reference when it's created
|
||||
|
||||
Plugin(CFUUIDRef factoryID)
|
||||
: interface(new QLGeneratorInterfaceStruct(Plugin_vtbl))
|
||||
, factoryID(factoryID) {
|
||||
CFPlugInAddInstanceForFactory(factoryID);
|
||||
}
|
||||
|
||||
|
||||
~Plugin() {
|
||||
delete interface;
|
||||
if (factoryID) {
|
||||
CFPlugInRemoveInstanceForFactory(factoryID);
|
||||
CFRelease(factoryID);
|
||||
}
|
||||
}
|
||||
|
||||
// IUnknown impl
|
||||
|
||||
HRESULT QueryInterface(REFIID iid, LPVOID* ppv) {
|
||||
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid);
|
||||
|
||||
if (CFEqual(interfaceID, kQLGeneratorCallbacksInterfaceID)) {
|
||||
*ppv = this;
|
||||
AddRef();
|
||||
CFRelease(interfaceID);
|
||||
return S_OK;
|
||||
}
|
||||
else {
|
||||
*ppv = nullptr;
|
||||
CFRelease(interfaceID);
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
ULONG AddRef() {
|
||||
return ++refCount;
|
||||
}
|
||||
|
||||
ULONG Release() {
|
||||
if (refCount == 1) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ASSERT(refCount != 0);
|
||||
return --refCount;
|
||||
}
|
||||
}
|
||||
|
||||
// QLGeneratorInterfaceStruct impl
|
||||
|
||||
static OSStatus GenerateThumbnailForURL(QLThumbnailRequestRef thumbnail,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options,
|
||||
CGSize maxSize) {
|
||||
CGImageRef image = desktop::get_thumbnail(url, options, maxSize);
|
||||
if (!image)
|
||||
return -1;
|
||||
|
||||
QLThumbnailRequestSetImage(thumbnail, image, nullptr);
|
||||
CGImageRelease(image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void CancelThumbnailGeneration(QLThumbnailRequestRef thumbnail) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
OSStatus GeneratePreviewForURL(QLPreviewRequestRef preview,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options) {
|
||||
CGImageRef image = desktop::get_thumbnail(url, options, CGSizeMake(0, 0));
|
||||
if (!image)
|
||||
return -1;
|
||||
|
||||
int w = CGImageGetWidth(image);
|
||||
int h = CGImageGetHeight(image);
|
||||
int wh = std::min(w, h);
|
||||
if (wh < 128) {
|
||||
w = 128 * w / wh;
|
||||
h = 128 * h / wh;
|
||||
}
|
||||
|
||||
CGContextRef cg = QLPreviewRequestCreateContext(preview, CGSizeMake(w, h), YES, options);
|
||||
CGContextSetInterpolationQuality(cg, kCGInterpolationNone);
|
||||
CGContextDrawImage(cg, CGRectMake(0, 0, w, h), image);
|
||||
QLPreviewRequestFlushContext(preview, cg);
|
||||
CGImageRelease(image);
|
||||
CGContextRelease(cg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CancelPreviewGeneration(QLPreviewRequestRef preview) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static HRESULT Plugin_QueryInterface(void* p, REFIID iid, LPVOID* ppv)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->QueryInterface(iid, ppv);
|
||||
}
|
||||
|
||||
static ULONG Plugin_AddRef(void* p)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->AddRef();
|
||||
}
|
||||
|
||||
static ULONG Plugin_Release(void* p)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->Release();
|
||||
}
|
||||
|
||||
static OSStatus Plugin_GenerateThumbnailForURL(void* p, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->GenerateThumbnailForURL(thumbnail, url, contentTypeUTI, options, maxSize);
|
||||
}
|
||||
|
||||
static void Plugin_CancelThumbnailGeneration(void* p, QLThumbnailRequestRef thumbnail)
|
||||
{
|
||||
ASSERT(p);
|
||||
reinterpret_cast<Plugin*>(p)->CancelThumbnailGeneration(thumbnail);
|
||||
}
|
||||
|
||||
static OSStatus Plugin_GeneratePreviewForURL(void* p, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->GeneratePreviewForURL(preview, url, contentTypeUTI, options);
|
||||
}
|
||||
|
||||
static void Plugin_CancelPreviewGeneration(void* p, QLPreviewRequestRef preview)
|
||||
{
|
||||
ASSERT(p);
|
||||
reinterpret_cast<Plugin*>(p)->CancelPreviewGeneration(preview);
|
||||
}
|
||||
|
||||
// This is the only public entry point of the framework/plugin (the
|
||||
// "QuickLookGeneratorPluginFactory" name is specified in the
|
||||
// Info.list file): the factory of objects. Similar than the Win32 COM
|
||||
// IClassFactory::CreateInstance()
|
||||
//
|
||||
// This function is used to create an instance of an object of
|
||||
// kQLGeneratorTypeID type, which should implement the
|
||||
// QLGeneratorInterfaceStruct interface.
|
||||
extern "C" void* QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,
|
||||
CFUUIDRef typeID)
|
||||
{
|
||||
if (CFEqual(typeID, kQLGeneratorTypeID)) {
|
||||
CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, CFSTR(PLUGIN_ID));
|
||||
auto plugin = new Plugin(uuid);
|
||||
CFRelease(uuid);
|
||||
return plugin;
|
||||
}
|
||||
return nullptr; // Unknown typeID
|
||||
}
|
21
src/desktop/osx/thumbnail.h
Normal file
21
src/desktop/osx/thumbnail.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DESKTOP_THUMBNAIL_H_INCLUDED
|
||||
#define DESKTOP_THUMBNAIL_H_INCLUDED
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
namespace desktop {
|
||||
|
||||
CGImageRef get_thumbnail(CFURLRef url,
|
||||
CFDictionaryRef options,
|
||||
CGSize maxSize);
|
||||
|
||||
} // namespace desktop
|
||||
|
||||
#endif
|
165
src/desktop/osx/thumbnail.mm
Normal file
165
src/desktop/osx/thumbnail.mm
Normal file
@ -0,0 +1,165 @@
|
||||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "thumbnail.h"
|
||||
|
||||
#include "dio/decode_delegate.h"
|
||||
#include "dio/decode_file.h"
|
||||
#include "dio/file_interface.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace desktop {
|
||||
|
||||
namespace {
|
||||
|
||||
class DecodeDelegate : public dio::DecodeDelegate {
|
||||
public:
|
||||
DecodeDelegate() : m_sprite(nullptr) { }
|
||||
~DecodeDelegate() { delete m_sprite; }
|
||||
|
||||
bool decodeOneFrame() override { return true; }
|
||||
void onSprite(doc::Sprite* sprite) override {
|
||||
m_sprite = sprite;
|
||||
}
|
||||
|
||||
doc::Sprite* sprite() { return m_sprite; }
|
||||
|
||||
private:
|
||||
doc::Sprite* m_sprite;
|
||||
};
|
||||
|
||||
class StreamAdaptor : public dio::FileInterface {
|
||||
public:
|
||||
StreamAdaptor(NSData* data)
|
||||
: m_data(data)
|
||||
, m_ok(m_data != nullptr)
|
||||
, m_pos(0) {
|
||||
}
|
||||
|
||||
bool ok() const {
|
||||
return m_ok;
|
||||
}
|
||||
|
||||
size_t tell() {
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
void seek(size_t absPos) {
|
||||
m_pos = absPos;
|
||||
}
|
||||
|
||||
uint8_t read8() {
|
||||
if (!m_ok)
|
||||
return 0;
|
||||
|
||||
if (m_pos < m_data.length)
|
||||
return ((const uint8_t*)m_data.bytes)[m_pos++];
|
||||
else {
|
||||
m_ok = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t readBytes(uint8_t* buf, size_t n) {
|
||||
if (!m_ok)
|
||||
return 0;
|
||||
|
||||
if (m_pos < m_data.length) {
|
||||
n = std::min(n, m_data.length - m_pos);
|
||||
|
||||
memcpy(buf, ((const uint8_t*)m_data.bytes)+m_pos, n);
|
||||
m_pos += n;
|
||||
return n;
|
||||
}
|
||||
else {
|
||||
m_ok = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void write8(uint8_t value) {
|
||||
// Do nothing, we don't write in the file
|
||||
}
|
||||
|
||||
NSData* m_data;
|
||||
bool m_ok;
|
||||
size_t m_pos;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
CGImageRef get_thumbnail(CFURLRef url,
|
||||
CFDictionaryRef options,
|
||||
CGSize maxSize)
|
||||
{
|
||||
auto data = [[NSData alloc] initWithContentsOfURL:(NSURL*)url];
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
doc::ImageRef image;
|
||||
int w, h;
|
||||
|
||||
try {
|
||||
DecodeDelegate delegate;
|
||||
StreamAdaptor adaptor(data);
|
||||
if (!dio::decode_file(&delegate, &adaptor))
|
||||
return nullptr;
|
||||
|
||||
const doc::Sprite* spr = delegate.sprite();
|
||||
if (!spr)
|
||||
return nullptr;
|
||||
|
||||
w = spr->width();
|
||||
h = spr->height();
|
||||
int wh = std::max<int>(w, h);
|
||||
int cx;
|
||||
if (maxSize.width && maxSize.height)
|
||||
cx = std::min<int>(maxSize.width, maxSize.height);
|
||||
else
|
||||
cx = wh;
|
||||
|
||||
image.reset(doc::Image::create(doc::IMAGE_RGB,
|
||||
cx * w / wh,
|
||||
cx * h / wh));
|
||||
image->clear(0);
|
||||
|
||||
render::Render render;
|
||||
render.setBgOptions(render::BgOptions::MakeTransparent());
|
||||
render.setProjection(render::Projection(doc::PixelRatio(1, 1),
|
||||
render::Zoom(cx, wh)));
|
||||
render.renderSprite(image.get(), spr, 0,
|
||||
gfx::ClipF(0, 0, 0, 0,
|
||||
image->width(), image->height()));
|
||||
|
||||
w = image->width();
|
||||
h = image->height();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
NSLog(@"AsepriteThumbnailer error: %s", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO Premultiply alpha because CGBitmapContextCreate doesn't
|
||||
// support unpremultiplied alpha (kCGImageAlphaFirst).
|
||||
|
||||
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||
CGContextRef gc = CGBitmapContextCreate(
|
||||
image->getPixelAddress(0, 0),
|
||||
w, h, 8, image->getRowStrideSize(), cs,
|
||||
kCGImageAlphaPremultipliedLast);
|
||||
CGColorSpaceRelease(cs);
|
||||
|
||||
CGImageRef img = CGBitmapContextCreateImage(gc);
|
||||
CGContextRelease(gc);
|
||||
return img;
|
||||
}
|
||||
|
||||
} // namespace desktop
|
Loading…
x
Reference in New Issue
Block a user