mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-27 15:35:21 +00:00
Move os/ft/gfx libraries to laf
This commit is contained in:
parent
5cb2d984f0
commit
a6fab8d1d9
@ -55,7 +55,6 @@ enable_testing()
|
||||
# CMakeCache.txt)
|
||||
|
||||
option(WITH_WEBP_SUPPORT "Enable support to load/save .webp files" on)
|
||||
option(WITH_GTK_FILE_DIALOG_SUPPORT "Enable support for the experimental native GTK File Dialog" off)
|
||||
option(WITH_DESKTOP_INTEGRATION "Enable desktop integration modules" off)
|
||||
option(WITH_QT_THUMBNAILER "Enable kde5/qt5 thumnailer" off)
|
||||
|
||||
@ -92,7 +91,7 @@ else()
|
||||
endif()
|
||||
|
||||
# Check valid gtk + libpng combination
|
||||
if(WITH_GTK_FILE_DIALOG_SUPPORT)
|
||||
if(LAF_OS_WITH_GTK)
|
||||
if(NOT USE_SHARED_LIBPNG)
|
||||
message(FATAL_ERROR "Cannot compile with gtk and static libpng, set USE_SHARED_LIBPNG=ON")
|
||||
endif()
|
||||
|
@ -8,8 +8,8 @@ function(find_tests dir dependencies)
|
||||
# Add gtest include directory so we can #include <gtest/gtest.h> in tests source code
|
||||
include_directories(${CMAKE_SOURCE_DIR}/third_party/gtest/include)
|
||||
|
||||
# See if the test is linked with "os-lib" library.
|
||||
list(FIND dependencies os-lib link_with_os)
|
||||
# See if the test is linked with "laf-os" library.
|
||||
list(FIND dependencies laf-os link_with_os)
|
||||
if(link_with_os)
|
||||
set(extra_definitions -DLINKED_WITH_OS_LIBRARY)
|
||||
endif()
|
||||
|
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit 2068f653224edfbfafc00a40a1d72a8e022d00d8
|
||||
Subproject commit f2a95de10e51b0dffdc3a14199fa489baf086c22
|
@ -102,12 +102,9 @@ add_subdirectory(filters)
|
||||
add_subdirectory(fixmath)
|
||||
add_subdirectory(flic)
|
||||
add_subdirectory(gen)
|
||||
add_subdirectory(gfx)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(render)
|
||||
add_subdirectory(dio)
|
||||
add_subdirectory(ft)
|
||||
add_subdirectory(os)
|
||||
add_subdirectory(ui)
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
@ -207,7 +204,6 @@ install(DIRECTORY ../data
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
include(FindTests)
|
||||
find_tests(gfx gfx-lib)
|
||||
find_tests(doc doc-lib)
|
||||
find_tests(render render-lib)
|
||||
find_tests(ui ui-lib)
|
||||
|
@ -575,10 +575,11 @@ target_link_libraries(app-lib
|
||||
filters-lib
|
||||
fixmath-lib
|
||||
flic-lib
|
||||
gfx-lib
|
||||
laf-gfx
|
||||
net-lib
|
||||
render-lib
|
||||
os-lib
|
||||
laf-ft
|
||||
laf-os
|
||||
ui-lib
|
||||
undo
|
||||
${CMARK_LIBRARIES}
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "app/color_utils.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "base/debug.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
@ -80,7 +81,7 @@ Color Color::fromGray(int g, int a)
|
||||
// static
|
||||
Color Color::fromIndex(int index)
|
||||
{
|
||||
assert(index >= 0);
|
||||
ASSERT(index >= 0);
|
||||
|
||||
Color color(Color::IndexType);
|
||||
color.m_value.index = index;
|
||||
|
@ -70,7 +70,7 @@ add_library(doc-lib
|
||||
# TODO Remove 'os' as dependency and move conversion_to_surface.cpp/h files
|
||||
# to other library/layer (render-lib? new conversion-lib?)
|
||||
target_link_libraries(doc-lib
|
||||
os-lib
|
||||
gfx-lib
|
||||
laf-os
|
||||
laf-gfx
|
||||
fixmath-lib
|
||||
laf-base)
|
||||
|
@ -1,11 +0,0 @@
|
||||
# Aseprite FreeType Wrapper
|
||||
# Copyright (C) 2017 David Capello
|
||||
|
||||
add_library(ft-lib
|
||||
lib.cpp
|
||||
stream.cpp)
|
||||
|
||||
target_link_libraries(ft-lib
|
||||
laf-base
|
||||
${FREETYPE_LIBRARIES}
|
||||
${HARFBUZZ_LIBRARIES})
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2016-2017 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,4 +0,0 @@
|
||||
# Aseprite FreeType Wrapper
|
||||
*Copyright (C) 2016-2017 David Capello*
|
||||
|
||||
> Distributed under [MIT license](LICENSE.txt)
|
@ -1,175 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_ALGORITHM_H_INCLUDED
|
||||
#define FT_ALGORITHM_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/string.h"
|
||||
#include "ft/freetype_headers.h"
|
||||
#include "ft/hb_shaper.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace ft {
|
||||
|
||||
template<typename FaceFT>
|
||||
class DefaultShaper {
|
||||
public:
|
||||
typedef typename FaceFT::Glyph Glyph;
|
||||
|
||||
DefaultShaper(FaceFT& face) : m_face(face) {
|
||||
}
|
||||
|
||||
bool initialize(const base::utf8_const_iterator& it,
|
||||
const base::utf8_const_iterator& end) {
|
||||
m_begin = m_it = it;
|
||||
m_end = end;
|
||||
return (m_it != end);
|
||||
}
|
||||
|
||||
bool nextChar() {
|
||||
++m_it;
|
||||
return (m_it != m_end);
|
||||
}
|
||||
|
||||
int unicodeChar() const {
|
||||
return *m_it;
|
||||
}
|
||||
|
||||
int charIndex() {
|
||||
return m_it - m_begin;
|
||||
}
|
||||
|
||||
unsigned int glyphIndex() {
|
||||
return m_face.cache().getGlyphIndex(m_face, unicodeChar());
|
||||
}
|
||||
|
||||
void glyphOffsetXY(Glyph* glyph) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) {
|
||||
x += glyph->ft_glyph->advance.x / double(1 << 16);
|
||||
y += glyph->ft_glyph->advance.y / double(1 << 16);
|
||||
}
|
||||
|
||||
private:
|
||||
FaceFT& m_face;
|
||||
base::utf8_const_iterator m_begin, m_end, m_it;
|
||||
};
|
||||
|
||||
template<typename FaceFT,
|
||||
typename Shaper = HBShaper<FaceFT> >
|
||||
class ForEachGlyph {
|
||||
public:
|
||||
typedef typename FaceFT::Glyph Glyph;
|
||||
|
||||
ForEachGlyph(FaceFT& face)
|
||||
: m_face(face)
|
||||
, m_shaper(face)
|
||||
, m_glyph(nullptr)
|
||||
, m_useKerning(FT_HAS_KERNING((FT_Face)face) ? true: false)
|
||||
, m_prevGlyph(0)
|
||||
, m_x(0.0), m_y(0.0) {
|
||||
}
|
||||
|
||||
~ForEachGlyph() {
|
||||
unloadGlyph();
|
||||
}
|
||||
|
||||
int unicodeChar() { return m_shaper.unicodeChar(); }
|
||||
int charIndex() { return m_shaper.charIndex(); }
|
||||
|
||||
const Glyph* glyph() const { return m_glyph; }
|
||||
|
||||
bool initialize(const base::utf8_const_iterator& it,
|
||||
const base::utf8_const_iterator& end) {
|
||||
bool res = m_shaper.initialize(it, end);
|
||||
if (res)
|
||||
prepareGlyph();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool nextChar() {
|
||||
m_prevGlyph = m_shaper.glyphIndex();
|
||||
|
||||
if (m_shaper.nextChar()) {
|
||||
prepareGlyph();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void prepareGlyph() {
|
||||
FT_UInt glyphIndex = m_shaper.glyphIndex();
|
||||
double initialX = m_x;
|
||||
|
||||
if (m_useKerning && m_prevGlyph && glyphIndex) {
|
||||
FT_Vector kerning;
|
||||
FT_Get_Kerning(m_face, m_prevGlyph, glyphIndex,
|
||||
FT_KERNING_DEFAULT, &kerning);
|
||||
m_x += kerning.x / 64.0;
|
||||
}
|
||||
|
||||
unloadGlyph();
|
||||
|
||||
// Load new glyph
|
||||
m_glyph = m_face.cache().loadGlyph(m_face, glyphIndex, m_face.antialias());
|
||||
if (m_glyph) {
|
||||
m_glyph->bitmap = &FT_BitmapGlyph(m_glyph->ft_glyph)->bitmap;
|
||||
m_glyph->x = m_x
|
||||
+ m_glyph->bearingX;
|
||||
m_glyph->y = m_y
|
||||
+ m_face.height()
|
||||
+ m_face.descender() // descender is negative
|
||||
- m_glyph->bearingY;
|
||||
|
||||
m_shaper.glyphOffsetXY(m_glyph);
|
||||
m_shaper.glyphAdvanceXY(m_glyph, m_x, m_y);
|
||||
|
||||
m_glyph->startX = initialX;
|
||||
m_glyph->endX = m_x;
|
||||
}
|
||||
}
|
||||
|
||||
void unloadGlyph() {
|
||||
if (m_glyph) {
|
||||
m_face.cache().doneGlyph(m_glyph);
|
||||
m_glyph = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FaceFT& m_face;
|
||||
Shaper m_shaper;
|
||||
Glyph* m_glyph;
|
||||
bool m_useKerning;
|
||||
FT_UInt m_prevGlyph;
|
||||
double m_x, m_y;
|
||||
};
|
||||
|
||||
template<typename FaceFT>
|
||||
gfx::Rect calc_text_bounds(FaceFT& face, const std::string& str) {
|
||||
gfx::Rect bounds(0, 0, 0, 0);
|
||||
ForEachGlyph<FaceFT> feg(face);
|
||||
if (feg.initialize(base::utf8_const_iterator(str.begin()),
|
||||
base::utf8_const_iterator(str.end()))) {
|
||||
do {
|
||||
if (auto glyph = feg.glyph())
|
||||
bounds |= gfx::Rect(int(glyph->x),
|
||||
int(glyph->y),
|
||||
glyph->bitmap->width,
|
||||
glyph->bitmap->rows);
|
||||
} while (feg.nextChar());
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
202
src/ft/face.h
202
src/ft/face.h
@ -1,202 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_FACE_H_INCLUDED
|
||||
#define FT_FACE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "base/disable_copying.h"
|
||||
#include "ft/freetype_headers.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace ft {
|
||||
|
||||
struct Glyph {
|
||||
FT_UInt glyph_index;
|
||||
FT_Glyph ft_glyph;
|
||||
FT_Bitmap* bitmap;
|
||||
double startX;
|
||||
double endX;
|
||||
double bearingX;
|
||||
double bearingY;
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
template<typename Cache>
|
||||
class FaceFT {
|
||||
public:
|
||||
typedef ft::Glyph Glyph;
|
||||
|
||||
FaceFT(FT_Face face) : m_face(face) {
|
||||
}
|
||||
|
||||
~FaceFT() {
|
||||
if (m_face)
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
|
||||
operator FT_Face() { return m_face; }
|
||||
FT_Face operator->() { return m_face; }
|
||||
|
||||
bool isValid() const {
|
||||
return (m_face != nullptr);
|
||||
}
|
||||
|
||||
bool antialias() const {
|
||||
return m_antialias;
|
||||
}
|
||||
|
||||
void setAntialias(bool antialias) {
|
||||
m_antialias = antialias;
|
||||
m_cache.invalidate();
|
||||
}
|
||||
|
||||
void setSize(int size) {
|
||||
FT_Set_Pixel_Sizes(m_face, size, size);
|
||||
m_cache.invalidate();
|
||||
}
|
||||
|
||||
double height() const {
|
||||
FT_Size_Metrics* metrics = &m_face->size->metrics;
|
||||
double em_size = 1.0 * m_face->units_per_EM;
|
||||
double y_scale = metrics->y_ppem / em_size;
|
||||
return int(m_face->height * y_scale) - 1;
|
||||
}
|
||||
|
||||
double ascender() const {
|
||||
FT_Size_Metrics* metrics = &m_face->size->metrics;
|
||||
double em_size = 1.0 * m_face->units_per_EM;
|
||||
double y_scale = metrics->y_ppem / em_size;
|
||||
return int(m_face->ascender * y_scale);
|
||||
}
|
||||
|
||||
double descender() const {
|
||||
FT_Size_Metrics* metrics = &m_face->size->metrics;
|
||||
double em_size = 1.0 * m_face->units_per_EM;
|
||||
double y_scale = metrics->y_ppem / em_size;
|
||||
return int(m_face->descender * y_scale);
|
||||
}
|
||||
|
||||
bool hasCodePoint(int codepoint) const {
|
||||
if (m_face) {
|
||||
codepoint = FT_Get_Char_Index(m_face, codepoint);
|
||||
return (codepoint != 0);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
Cache& cache() { return m_cache; }
|
||||
|
||||
protected:
|
||||
FT_Face m_face;
|
||||
bool m_antialias;
|
||||
Cache m_cache;
|
||||
|
||||
private:
|
||||
DISABLE_COPYING(FaceFT);
|
||||
};
|
||||
|
||||
class NoCache {
|
||||
public:
|
||||
void invalidate() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
FT_UInt getGlyphIndex(FT_Face face, int charCode) {
|
||||
return FT_Get_Char_Index(face, charCode);
|
||||
}
|
||||
|
||||
Glyph* loadGlyph(FT_Face face, FT_UInt glyphIndex, bool antialias) {
|
||||
FT_Error err = FT_Load_Glyph(
|
||||
face, glyphIndex,
|
||||
FT_LOAD_RENDER |
|
||||
(antialias ? FT_LOAD_TARGET_NORMAL:
|
||||
FT_LOAD_TARGET_MONO));
|
||||
if (err)
|
||||
return nullptr;
|
||||
|
||||
FT_Glyph ft_glyph;
|
||||
err = FT_Get_Glyph(face->glyph, &ft_glyph);
|
||||
if (err)
|
||||
return nullptr;
|
||||
|
||||
if (ft_glyph->format != FT_GLYPH_FORMAT_BITMAP) {
|
||||
err = FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
|
||||
if (!err) {
|
||||
FT_Done_Glyph(ft_glyph);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
m_glyph.ft_glyph = ft_glyph;
|
||||
m_glyph.bearingX = face->glyph->metrics.horiBearingX / 64.0;
|
||||
m_glyph.bearingY = face->glyph->metrics.horiBearingY / 64.0;
|
||||
|
||||
return &m_glyph;
|
||||
}
|
||||
|
||||
void doneGlyph(Glyph* glyph) {
|
||||
ASSERT(glyph);
|
||||
FT_Done_Glyph(glyph->ft_glyph);
|
||||
}
|
||||
|
||||
private:
|
||||
Glyph m_glyph;
|
||||
};
|
||||
|
||||
class SimpleCache : public NoCache {
|
||||
public:
|
||||
~SimpleCache() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
for (auto& it : m_glyphMap) {
|
||||
FT_Done_Glyph(it.second->ft_glyph);
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
m_glyphMap.clear();
|
||||
}
|
||||
|
||||
Glyph* loadGlyph(FT_Face face, FT_UInt glyphIndex, bool antialias) {
|
||||
auto it = m_glyphMap.find(glyphIndex);
|
||||
if (it != m_glyphMap.end())
|
||||
return it->second;
|
||||
|
||||
Glyph* glyph = NoCache::loadGlyph(face, glyphIndex, antialias);
|
||||
if (!glyph)
|
||||
return nullptr;
|
||||
|
||||
FT_Glyph new_ft_glyph = nullptr;
|
||||
FT_Glyph_Copy(glyph->ft_glyph, &new_ft_glyph);
|
||||
if (!new_ft_glyph)
|
||||
return nullptr;
|
||||
|
||||
Glyph* newGlyph = new Glyph(*glyph);
|
||||
newGlyph->ft_glyph = new_ft_glyph;
|
||||
|
||||
m_glyphMap[glyphIndex] = newGlyph;
|
||||
FT_Done_Glyph(glyph->ft_glyph);
|
||||
|
||||
return newGlyph;
|
||||
}
|
||||
|
||||
void doneGlyph(Glyph* glyph) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<FT_UInt, Glyph*> m_glyphMap;
|
||||
};
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
@ -1,15 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_FREETYPE_HEADERS_H_INCLUDED
|
||||
#define FT_FREETYPE_HEADERS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_GLYPH_H
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#endif
|
@ -1,44 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_HB_FACE_H_INCLUDED
|
||||
#define FT_HB_FACE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/string.h"
|
||||
#include "ft/face.h"
|
||||
|
||||
#include <hb.h>
|
||||
#include <hb-ft.h>
|
||||
|
||||
namespace ft {
|
||||
|
||||
template<typename FaceFT>
|
||||
class HBFace : public FaceFT {
|
||||
public:
|
||||
HBFace(FT_Face face) : FaceFT(face) {
|
||||
m_font = (face ? hb_ft_font_create((FT_Face)face, nullptr): nullptr);
|
||||
m_buffer = (face ? hb_buffer_create(): nullptr);
|
||||
}
|
||||
|
||||
~HBFace() {
|
||||
if (m_buffer) hb_buffer_destroy(m_buffer);
|
||||
if (m_font) hb_font_destroy(m_font);
|
||||
}
|
||||
|
||||
hb_font_t* font() const { return m_font; }
|
||||
hb_buffer_t* buffer() const { return m_buffer; }
|
||||
|
||||
private:
|
||||
hb_buffer_t* m_buffer;
|
||||
hb_font_t* m_font;
|
||||
};
|
||||
|
||||
typedef HBFace<FaceFT<SimpleCache> > Face;
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
@ -1,90 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_HB_SHAPER_H_INCLUDED
|
||||
#define FT_HB_SHAPER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "ft/hb_face.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ft {
|
||||
|
||||
template<typename HBFace>
|
||||
class HBShaper {
|
||||
public:
|
||||
HBShaper(HBFace& face)
|
||||
: m_face(face)
|
||||
, m_unicodeFuncs(hb_buffer_get_unicode_funcs(face.buffer())) {
|
||||
}
|
||||
|
||||
bool initialize(const base::utf8_const_iterator& begin,
|
||||
const base::utf8_const_iterator& end) {
|
||||
m_index = 0;
|
||||
if (begin == end)
|
||||
return false;
|
||||
|
||||
hb_buffer_t* buf = m_face.buffer();
|
||||
|
||||
hb_buffer_reset(buf);
|
||||
for (auto it=begin; it!=end; ++it)
|
||||
hb_buffer_add(buf, *it, it - begin);
|
||||
|
||||
// Just in case we're compiling with an old harfbuzz version
|
||||
#ifdef HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS
|
||||
hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
|
||||
#endif
|
||||
hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
|
||||
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
|
||||
hb_buffer_guess_segment_properties(buf);
|
||||
|
||||
hb_shape(m_face.font(), buf, nullptr, 0);
|
||||
|
||||
m_glyphInfo = hb_buffer_get_glyph_infos(buf, &m_glyphCount);
|
||||
m_glyphPos = hb_buffer_get_glyph_positions(buf, &m_glyphCount);
|
||||
return (m_glyphCount > 0);
|
||||
}
|
||||
|
||||
bool nextChar() {
|
||||
++m_index;
|
||||
return (m_index < m_glyphCount);
|
||||
}
|
||||
|
||||
int unicodeChar() const {
|
||||
return m_glyphInfo[m_index].codepoint;
|
||||
}
|
||||
|
||||
int charIndex() {
|
||||
return m_glyphInfo[m_index].cluster;
|
||||
}
|
||||
|
||||
unsigned int glyphIndex() const {
|
||||
return m_glyphInfo[m_index].codepoint;
|
||||
}
|
||||
|
||||
void glyphOffsetXY(Glyph* glyph) {
|
||||
glyph->x += m_glyphPos[m_index].x_offset / 64.0;
|
||||
glyph->y += m_glyphPos[m_index].y_offset / 64.0;
|
||||
}
|
||||
|
||||
void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) {
|
||||
x += m_glyphPos[m_index].x_advance / 64.0;
|
||||
y += m_glyphPos[m_index].y_advance / 64.0;
|
||||
}
|
||||
|
||||
private:
|
||||
HBFace& m_face;
|
||||
hb_glyph_info_t* m_glyphInfo;
|
||||
hb_glyph_position_t* m_glyphPos;
|
||||
unsigned int m_glyphCount;
|
||||
unsigned int m_index;
|
||||
hb_unicode_funcs_t* m_unicodeFuncs;
|
||||
};
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
@ -1,45 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "ft/lib.h"
|
||||
|
||||
#include "base/log.h"
|
||||
#include "ft/stream.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ft {
|
||||
|
||||
Lib::Lib()
|
||||
: m_ft(nullptr)
|
||||
{
|
||||
FT_Init_FreeType(&m_ft);
|
||||
}
|
||||
|
||||
Lib::~Lib()
|
||||
{
|
||||
if (m_ft)
|
||||
FT_Done_FreeType(m_ft);
|
||||
}
|
||||
|
||||
FT_Face Lib::open(const std::string& filename)
|
||||
{
|
||||
FT_Stream stream = ft::open_stream(filename);
|
||||
FT_Open_Args args;
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.flags = FT_OPEN_STREAM;
|
||||
args.stream = stream;
|
||||
|
||||
LOG(VERBOSE) << "FT: Loading font '" << filename << "'\n";
|
||||
|
||||
FT_Face face = nullptr;
|
||||
FT_Error err = FT_Open_Face(m_ft, &args, 0, &face);
|
||||
if (!err)
|
||||
FT_Select_Charmap(face, FT_ENCODING_UNICODE);
|
||||
return face;
|
||||
}
|
||||
|
||||
} // namespace ft
|
35
src/ft/lib.h
35
src/ft/lib.h
@ -1,35 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_LIB_H_INCLUDED
|
||||
#define FT_LIB_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/disable_copying.h"
|
||||
#include "ft/freetype_headers.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ft {
|
||||
|
||||
class Lib {
|
||||
public:
|
||||
Lib();
|
||||
~Lib();
|
||||
|
||||
operator FT_Library() { return m_ft; }
|
||||
|
||||
FT_Face open(const std::string& filename);
|
||||
|
||||
private:
|
||||
FT_Library m_ft;
|
||||
|
||||
DISABLE_COPYING(Lib);
|
||||
};
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
@ -1,75 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2016-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "ft/stream.h"
|
||||
|
||||
#include "base/file_handle.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
|
||||
#define STREAM_FILE(stream) ((FILE*)stream->descriptor.pointer)
|
||||
|
||||
namespace ft {
|
||||
|
||||
static void ft_stream_close(FT_Stream stream)
|
||||
{
|
||||
fclose(STREAM_FILE(stream));
|
||||
free(stream);
|
||||
}
|
||||
|
||||
static unsigned long ft_stream_io(FT_Stream stream,
|
||||
unsigned long offset,
|
||||
unsigned char* buffer,
|
||||
unsigned long count)
|
||||
{
|
||||
if (!count && offset > stream->size)
|
||||
return 1;
|
||||
|
||||
FILE* file = STREAM_FILE(stream);
|
||||
if (stream->pos != offset)
|
||||
fseek(file, (long)offset, SEEK_SET);
|
||||
|
||||
return (unsigned long)fread(buffer, 1, count, file);
|
||||
}
|
||||
|
||||
FT_Stream open_stream(const std::string& utf8Filename)
|
||||
{
|
||||
FT_Stream stream = nullptr;
|
||||
stream = (FT_Stream)malloc(sizeof(*stream));
|
||||
if(!stream)
|
||||
return nullptr;
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
|
||||
TRACE("FT: Loading font %s... ", utf8Filename.c_str());
|
||||
|
||||
FILE* file = base::open_file_raw(utf8Filename, "rb");
|
||||
if (!file) {
|
||||
free(stream);
|
||||
TRACE("FAIL\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
stream->size = (unsigned long)ftell(file);
|
||||
if (!stream->size) {
|
||||
fclose(file);
|
||||
free(stream);
|
||||
TRACE("FAIL\n");
|
||||
return nullptr;
|
||||
}
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
stream->descriptor.pointer = file;
|
||||
stream->base = nullptr;
|
||||
stream->pos = 0;
|
||||
stream->read = ft_stream_io;
|
||||
stream->close = ft_stream_close;
|
||||
|
||||
TRACE("OK\n");
|
||||
return stream;
|
||||
}
|
||||
|
||||
} // namespace ft
|
@ -1,21 +0,0 @@
|
||||
// Aseprite FreeType Wrapper
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef FT_STREAM_H_INCLUDED
|
||||
#define FT_STREAM_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "ft/freetype_headers.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ft {
|
||||
|
||||
FT_Stream open_stream(const std::string& utf8Filename);
|
||||
|
||||
} // namespace ft
|
||||
|
||||
#endif
|
@ -1,13 +0,0 @@
|
||||
# Aseprite
|
||||
# Copyright (C) 2001-2017 David Capello
|
||||
|
||||
add_library(gfx-lib
|
||||
hsl.cpp
|
||||
hsv.cpp
|
||||
packing_rects.cpp
|
||||
region.cpp
|
||||
rgb.cpp)
|
||||
|
||||
target_link_libraries(gfx-lib
|
||||
laf-base
|
||||
${PIXMAN_LIBRARY})
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2001-2016 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,6 +0,0 @@
|
||||
# Aseprite Gfx Library
|
||||
*Copyright (C) 2001-2016 David Capello*
|
||||
|
||||
> Distributed under [MIT license](LICENSE.txt)
|
||||
|
||||
The `gfx::Region` class depends on pixman library.
|
205
src/gfx/border.h
205
src/gfx/border.h
@ -1,205 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2013, 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_BORDER_H_INCLUDED
|
||||
#define GFX_BORDER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T>
|
||||
class SizeT;
|
||||
|
||||
template<typename T>
|
||||
class BorderT
|
||||
{
|
||||
public:
|
||||
BorderT() :
|
||||
m_left(0),
|
||||
m_top(0),
|
||||
m_right(0),
|
||||
m_bottom(0) {
|
||||
}
|
||||
|
||||
BorderT(const T& left, const T& top, const T& right, const T& bottom) :
|
||||
m_left(left),
|
||||
m_top(top),
|
||||
m_right(right),
|
||||
m_bottom(bottom) {
|
||||
}
|
||||
|
||||
explicit BorderT(const T& allSides) :
|
||||
m_left(allSides),
|
||||
m_top(allSides),
|
||||
m_right(allSides),
|
||||
m_bottom(allSides) {
|
||||
}
|
||||
|
||||
T left() const { return m_left; };
|
||||
T top() const { return m_top; };
|
||||
T right() const { return m_right; };
|
||||
T bottom() const { return m_bottom; };
|
||||
|
||||
T width() const { return m_left + m_right; };
|
||||
T height() const { return m_top + m_bottom; };
|
||||
|
||||
void left(const T& left) { m_left = left; }
|
||||
void top(const T& top) { m_top = top; }
|
||||
void right(const T& right) { m_right = right; }
|
||||
void bottom(const T& bottom) { m_bottom = bottom; }
|
||||
|
||||
SizeT<T> size() const {
|
||||
return SizeT<T>(m_left + m_right, m_top + m_bottom);
|
||||
}
|
||||
|
||||
const BorderT& operator+=(const BorderT& br) {
|
||||
m_left += br.m_left;
|
||||
m_top += br.m_top;
|
||||
m_right += br.m_right;
|
||||
m_bottom += br.m_bottom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator-=(const BorderT& br) {
|
||||
m_left -= br.m_left;
|
||||
m_top -= br.m_top;
|
||||
m_right -= br.m_right;
|
||||
m_bottom -= br.m_bottom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator*=(const BorderT& br) {
|
||||
m_left *= br.m_left;
|
||||
m_top *= br.m_top;
|
||||
m_right *= br.m_right;
|
||||
m_bottom *= br.m_bottom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator/=(const BorderT& br) {
|
||||
m_left /= br.m_left;
|
||||
m_top /= br.m_top;
|
||||
m_right /= br.m_right;
|
||||
m_bottom /= br.m_bottom;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator+=(const T& value) {
|
||||
m_left += value;
|
||||
m_top += value;
|
||||
m_right += value;
|
||||
m_bottom += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator-=(const T& value) {
|
||||
m_left -= value;
|
||||
m_top -= value;
|
||||
m_right -= value;
|
||||
m_bottom -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator*=(const T& value) {
|
||||
m_left *= value;
|
||||
m_top *= value;
|
||||
m_right *= value;
|
||||
m_bottom *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const BorderT& operator/=(const T& value) {
|
||||
m_left /= value;
|
||||
m_top /= value;
|
||||
m_right /= value;
|
||||
m_bottom /= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BorderT operator+(const BorderT& br) const {
|
||||
return BorderT(m_left + br.left(),
|
||||
m_top + br.top(),
|
||||
m_right + br.right(),
|
||||
m_bottom + br.bottom());
|
||||
}
|
||||
|
||||
BorderT operator-(const BorderT& br) const {
|
||||
return BorderT(m_left - br.left(),
|
||||
m_top - br.top(),
|
||||
m_right - br.right(),
|
||||
m_bottom - br.bottom());
|
||||
}
|
||||
|
||||
BorderT operator*(const BorderT& br) const {
|
||||
return BorderT(m_left * br.left(),
|
||||
m_top * br.top(),
|
||||
m_right * br.right(),
|
||||
m_bottom * br.bottom());
|
||||
}
|
||||
|
||||
BorderT operator/(const BorderT& br) const {
|
||||
return BorderT(m_left / br.left(),
|
||||
m_top / br.top(),
|
||||
m_right / br.right(),
|
||||
m_bottom / br.bottom());
|
||||
}
|
||||
|
||||
BorderT operator+(const T& value) const {
|
||||
return BorderT(m_left + value,
|
||||
m_top + value,
|
||||
m_right + value,
|
||||
m_bottom + value);
|
||||
}
|
||||
|
||||
BorderT operator-(const T& value) const {
|
||||
return BorderT(m_left - value,
|
||||
m_top - value,
|
||||
m_right - value,
|
||||
m_bottom - value);
|
||||
}
|
||||
|
||||
BorderT operator*(const T& value) const {
|
||||
return BorderT(m_left * value,
|
||||
m_top * value,
|
||||
m_right * value,
|
||||
m_bottom * value);
|
||||
}
|
||||
|
||||
BorderT operator/(const T& value) const {
|
||||
return BorderT(m_left / value,
|
||||
m_top / value,
|
||||
m_right / value,
|
||||
m_bottom / value);
|
||||
}
|
||||
|
||||
BorderT operator-() const {
|
||||
return BorderT(-m_left, -m_top, -m_right, -m_bottom);
|
||||
}
|
||||
|
||||
bool operator==(const BorderT& br) const {
|
||||
return
|
||||
m_left == br.m_left && m_top == br.m_top &&
|
||||
m_right == br.m_right && m_bottom == br.m_bottom;
|
||||
}
|
||||
|
||||
bool operator!=(const BorderT& br) const {
|
||||
return
|
||||
m_left != br.m_left || m_top != br.m_top ||
|
||||
m_right != br.m_right || m_bottom != br.m_bottom;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_left;
|
||||
T m_top;
|
||||
T m_right;
|
||||
T m_bottom;
|
||||
};
|
||||
|
||||
typedef BorderT<int> Border;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
138
src/gfx/clip.h
138
src/gfx/clip.h
@ -1,138 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_CLIP_H_INCLUDED
|
||||
#define GFX_CLIP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T>
|
||||
class ClipT {
|
||||
public:
|
||||
PointT<T> dst;
|
||||
PointT<T> src;
|
||||
SizeT<T> size;
|
||||
|
||||
ClipT()
|
||||
: dst(0, 0)
|
||||
, src(0, 0)
|
||||
, size(0, 0) {
|
||||
}
|
||||
|
||||
ClipT(T w, T h)
|
||||
: dst(0, 0)
|
||||
, src(0, 0)
|
||||
, size(w, h) {
|
||||
}
|
||||
|
||||
ClipT(T dst_x, T dst_y, T src_x, T src_y, T w, T h)
|
||||
: dst(dst_x, dst_y)
|
||||
, src(src_x, src_y)
|
||||
, size(w, h) {
|
||||
}
|
||||
|
||||
ClipT(T dst_x, T dst_y, const RectT<T>& srcBounds)
|
||||
: dst(dst_x, dst_y)
|
||||
, src(srcBounds.x, srcBounds.y)
|
||||
, size(srcBounds.w, srcBounds.h) {
|
||||
}
|
||||
|
||||
ClipT(const PointT<T>& dst, const PointT<T>& src, const SizeT<T>& size)
|
||||
: dst(dst)
|
||||
, src(src)
|
||||
, size(size) {
|
||||
}
|
||||
|
||||
ClipT(const PointT<T>& dst, const RectT<T>& srcBounds)
|
||||
: dst(dst)
|
||||
, src(srcBounds.x, srcBounds.y)
|
||||
, size(srcBounds.w, srcBounds.h) {
|
||||
}
|
||||
|
||||
ClipT(const RectT<T>& bounds)
|
||||
: dst(bounds.x, bounds.y)
|
||||
, src(bounds.x, bounds.y)
|
||||
, size(bounds.w, bounds.h) {
|
||||
}
|
||||
|
||||
template<typename T2>
|
||||
ClipT(const ClipT<T2>& other)
|
||||
: dst(other.dst)
|
||||
, src(other.src)
|
||||
, size(other.size) {
|
||||
}
|
||||
|
||||
RectT<T> dstBounds() const { return RectT<T>(dst, size); }
|
||||
RectT<T> srcBounds() const { return RectT<T>(src, size); }
|
||||
|
||||
bool operator==(const ClipT<T>& other) const {
|
||||
return (dst == other.dst &&
|
||||
src == other.src &&
|
||||
size == other.size);
|
||||
}
|
||||
|
||||
bool clip(
|
||||
// Available area
|
||||
T avail_dst_w,
|
||||
T avail_dst_h,
|
||||
T avail_src_w,
|
||||
T avail_src_h) {
|
||||
// Clip srcBounds
|
||||
|
||||
if (src.x < T(0)) {
|
||||
size.w += src.x;
|
||||
dst.x -= src.x;
|
||||
src.x = T(0);
|
||||
}
|
||||
|
||||
if (src.y < T(0)) {
|
||||
size.h += src.y;
|
||||
dst.y -= src.y;
|
||||
src.y = T(0);
|
||||
}
|
||||
|
||||
if (src.x + size.w > avail_src_w)
|
||||
size.w -= src.x + size.w - avail_src_w;
|
||||
|
||||
if (src.y + size.h > avail_src_h)
|
||||
size.h -= src.y + size.h - avail_src_h;
|
||||
|
||||
// Clip dstBounds
|
||||
|
||||
if (dst.x < T(0)) {
|
||||
size.w += dst.x;
|
||||
src.x -= dst.x;
|
||||
dst.x = T(0);
|
||||
}
|
||||
|
||||
if (dst.y < T(0)) {
|
||||
size.h += dst.y;
|
||||
src.y -= dst.y;
|
||||
dst.y = T(0);
|
||||
}
|
||||
|
||||
if (dst.x + size.w > avail_dst_w)
|
||||
size.w -= dst.x + size.w - avail_dst_w;
|
||||
|
||||
if (dst.y + size.h > avail_dst_h)
|
||||
size.h -= dst.y + size.h - avail_dst_h;
|
||||
|
||||
return (size.w > T(0) && size.h > T(0));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef ClipT<int> Clip;
|
||||
typedef ClipT<double> ClipF;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,104 +0,0 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/clip.h"
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const Clip& area)
|
||||
{
|
||||
return os << "("
|
||||
<< area.dst.x << ", "
|
||||
<< area.dst.y << ", "
|
||||
<< area.src.x << ", "
|
||||
<< area.src.y << ", "
|
||||
<< area.size.w << ", "
|
||||
<< area.size.h << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(ScaledClip, WithoutClip)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(0, 0, 0, 0, 16, 16);
|
||||
EXPECT_TRUE(area.clip(16, 16, 16, 16));
|
||||
EXPECT_EQ(Clip(0, 0, 0, 0, 16, 16), area);
|
||||
|
||||
area = Clip(2, 2, 0, 0, 16, 16);
|
||||
EXPECT_TRUE(area.clip(32, 32, 16, 16));
|
||||
EXPECT_EQ(Clip(2, 2, 0, 0, 16, 16), area);
|
||||
}
|
||||
|
||||
TEST(ScaledClip, FullyClipped)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(32, 32, 0, 0, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
|
||||
area = Clip(-16, -16, 0, 0, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
|
||||
area = Clip(0, 0, 16, 16, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
}
|
||||
|
||||
TEST(ScaledClip, WithoutZoomWithClip)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(2, 3, 1, -1, 4, 3);
|
||||
EXPECT_TRUE(area.clip(30, 29, 16, 16));
|
||||
EXPECT_EQ(Clip(2, 4, 1, 0, 4, 2), area);
|
||||
|
||||
area = Clip(0, 0, -1, -4, 8, 5);
|
||||
EXPECT_TRUE(area.clip(3, 32, 8, 8));
|
||||
EXPECT_EQ(Clip(1, 4, 0, 0, 2, 1), area);
|
||||
}
|
||||
|
||||
TEST(ScaledClip, Zoom)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(0, 0, 0, 0, 32, 32);
|
||||
EXPECT_TRUE(area.clip(32, 32, 16, 16));
|
||||
EXPECT_EQ(Clip(0, 0, 0, 0, 16, 16), area);
|
||||
|
||||
area = Clip(0, 0, 1, 2, 32, 32);
|
||||
EXPECT_TRUE(area.clip(32, 32, 32, 32));
|
||||
EXPECT_EQ(Clip(0, 0, 1, 2, 31, 30), area);
|
||||
|
||||
// X:
|
||||
// -1 0 1 2 3 4 5
|
||||
// [ ]
|
||||
// a[a a b] b c c c DST
|
||||
// a a[a b b]b c c c SRC
|
||||
//
|
||||
// Y:
|
||||
// -1 0 1 2 3
|
||||
// [ ]
|
||||
// a[a a]b b b DST
|
||||
// [a a]a b b b c c SRC
|
||||
area = Clip(-1, 1, 1, -1, 4, 4);
|
||||
EXPECT_TRUE(area.clip(6, 4, 9, 9));
|
||||
EXPECT_EQ(Clip(0, 2, 2, 0, 3, 2), area);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_COLOR_H_INCLUDED
|
||||
#define GFX_COLOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/ints.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
typedef uint32_t Color;
|
||||
typedef uint8_t ColorComponent;
|
||||
|
||||
static const int ColorRShift = 0;
|
||||
static const int ColorGShift = 8;
|
||||
static const int ColorBShift = 16;
|
||||
static const int ColorAShift = 24;
|
||||
|
||||
static const Color ColorNone = Color(0);
|
||||
|
||||
inline Color rgba(ColorComponent r, ColorComponent g, ColorComponent b, ColorComponent a = 255) {
|
||||
return Color((r << ColorRShift) |
|
||||
(g << ColorGShift) |
|
||||
(b << ColorBShift) |
|
||||
(a << ColorAShift));
|
||||
}
|
||||
|
||||
inline ColorComponent getr(Color c) { return (c >> ColorRShift) & 0xff; }
|
||||
inline ColorComponent getg(Color c) { return (c >> ColorGShift) & 0xff; }
|
||||
inline ColorComponent getb(Color c) { return (c >> ColorBShift) & 0xff; }
|
||||
inline ColorComponent geta(Color c) { return (c >> ColorAShift) & 0xff; }
|
||||
|
||||
inline bool is_transparent(Color c) { return geta(c) == 0; }
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif // GFX_COLOR_H_INCLUDED
|
@ -1,29 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_FWD_H_INCLUDED
|
||||
#define GFX_FWD_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T> class BorderT;
|
||||
template<typename T> class ClipT;
|
||||
template<typename T> class PointT;
|
||||
template<typename T> class RectT;
|
||||
template<typename T> class SizeT;
|
||||
|
||||
typedef BorderT<int> Border;
|
||||
typedef ClipT<int> Clip;
|
||||
typedef PointT<int> Point;
|
||||
typedef RectT<int> Rect;
|
||||
typedef SizeT<int> Size;
|
||||
|
||||
class Region;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,88 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/hsl.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
using namespace std;
|
||||
|
||||
Hsl::Hsl(double hue, double saturation, double lightness)
|
||||
: m_hue(hue)
|
||||
, m_saturation(MID(0.0, saturation, 1.0))
|
||||
, m_lightness(MID(0.0, lightness, 1.0))
|
||||
{
|
||||
while (m_hue < 0.0)
|
||||
m_hue += 360.0;
|
||||
m_hue = std::fmod(m_hue, 360.0);
|
||||
}
|
||||
|
||||
Hsl::Hsl(const Rgb& rgb)
|
||||
{
|
||||
int M = rgb.maxComponent();
|
||||
int m = rgb.minComponent();
|
||||
int c = M - m;
|
||||
double chroma = double(c) / 255.0;
|
||||
double hue_prime = 0.0;
|
||||
double h, s, l;
|
||||
double r, g, b;
|
||||
|
||||
l = double((M + m) / 255.0) / 2.0;
|
||||
|
||||
if (c == 0) {
|
||||
h = 0.0; // Undefined Hue because max == min
|
||||
s = 0.0;
|
||||
}
|
||||
else {
|
||||
r = double(rgb.red()) / 255.0;
|
||||
g = double(rgb.green()) / 255.0;
|
||||
b = double(rgb.blue()) / 255.0;
|
||||
s = chroma / (1-std::fabs(2.0*l-1.0));
|
||||
|
||||
if (M == rgb.red()) {
|
||||
hue_prime = (g - b) / chroma;
|
||||
|
||||
while (hue_prime < 0.0)
|
||||
hue_prime += 6.0;
|
||||
hue_prime = std::fmod(hue_prime, 6.0);
|
||||
}
|
||||
else if (M == rgb.green()) {
|
||||
hue_prime = ((b - r) / chroma) + 2.0;
|
||||
}
|
||||
else if (M == rgb.blue()) {
|
||||
hue_prime = ((r - g) / chroma) + 4.0;
|
||||
}
|
||||
|
||||
h = hue_prime * 60.0;
|
||||
}
|
||||
|
||||
m_hue = h;
|
||||
m_saturation = s;
|
||||
m_lightness = l;
|
||||
}
|
||||
|
||||
int Hsl::hueInt() const
|
||||
{
|
||||
return int(std::floor(m_hue + 0.5));
|
||||
}
|
||||
|
||||
int Hsl::saturationInt() const
|
||||
{
|
||||
return int(std::floor(m_saturation*100.0 + 0.5));
|
||||
}
|
||||
|
||||
int Hsl::lightnessInt() const
|
||||
{
|
||||
return int(std::floor(m_lightness*100.0 + 0.5));
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,81 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_HSL_H_INCLUDED
|
||||
#define GFX_HSL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/base.h" // MID
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Rgb;
|
||||
|
||||
class Hsl {
|
||||
public:
|
||||
Hsl()
|
||||
: m_hue(0.0)
|
||||
, m_saturation(0.0)
|
||||
, m_lightness(0.0)
|
||||
{ }
|
||||
|
||||
Hsl(double hue, double saturation, double lightness);
|
||||
|
||||
Hsl(const Hsl& hsl)
|
||||
: m_hue(hsl.hue())
|
||||
, m_saturation(hsl.saturation())
|
||||
, m_lightness(hsl.lightness())
|
||||
{ }
|
||||
|
||||
// RGB to HSL conversion
|
||||
explicit Hsl(const Rgb& rgb);
|
||||
|
||||
// Returns color's hue, a value from 0 to 360
|
||||
double hue() const { return m_hue; }
|
||||
|
||||
// Returns color's saturation, a value from 0 to 100
|
||||
double saturation() const { return m_saturation; }
|
||||
|
||||
// Returns color's lightness, a value from 0 to 100
|
||||
double lightness() const { return m_lightness; }
|
||||
|
||||
// Integer getters, hue=[0,360), saturation=[0,100], value=[0,100]
|
||||
int hueInt() const;
|
||||
int saturationInt() const;
|
||||
int lightnessInt() const;
|
||||
|
||||
void hue(double hue) {
|
||||
m_hue = MID(0.0, hue, 360.0);
|
||||
}
|
||||
|
||||
void saturation(double saturation) {
|
||||
m_saturation = MID(0.0, saturation, 1.0);
|
||||
}
|
||||
|
||||
void lightness(double lightness) {
|
||||
m_lightness = MID(0.0, lightness, 1.0);
|
||||
}
|
||||
|
||||
// The comparison is done through the integer value of each component.
|
||||
bool operator==(const Hsl& other) const {
|
||||
return (hueInt() == other.hueInt() &&
|
||||
saturationInt() == other.saturationInt() &&
|
||||
lightnessInt() == other.lightnessInt());
|
||||
}
|
||||
|
||||
bool operator!=(const Hsl& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_hue;
|
||||
double m_saturation;
|
||||
double m_lightness;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,89 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
using namespace std;
|
||||
|
||||
Hsv::Hsv(double hue, double saturation, double value)
|
||||
: m_hue(hue)
|
||||
, m_saturation(MID(0.0, saturation, 1.0))
|
||||
, m_value(MID(0.0, value, 1.0))
|
||||
{
|
||||
while (m_hue < 0.0)
|
||||
m_hue += 360.0;
|
||||
m_hue = std::fmod(m_hue, 360.0);
|
||||
}
|
||||
|
||||
// Reference: http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
Hsv::Hsv(const Rgb& rgb)
|
||||
{
|
||||
int M = rgb.maxComponent();
|
||||
int m = rgb.minComponent();
|
||||
int c = M - m;
|
||||
double chroma = double(c) / 255.0;
|
||||
double hue_prime = 0.0;
|
||||
double h, s, v;
|
||||
double r, g, b;
|
||||
|
||||
v = double(M) / 255.0;
|
||||
|
||||
if (c == 0) {
|
||||
h = 0.0; // Undefined Hue because max == min
|
||||
s = 0.0;
|
||||
}
|
||||
else {
|
||||
r = double(rgb.red()) / 255.0;
|
||||
g = double(rgb.green()) / 255.0;
|
||||
b = double(rgb.blue()) / 255.0;
|
||||
s = chroma / v;
|
||||
|
||||
if (M == rgb.red()) {
|
||||
hue_prime = (g - b) / chroma;
|
||||
|
||||
while (hue_prime < 0.0)
|
||||
hue_prime += 6.0;
|
||||
hue_prime = std::fmod(hue_prime, 6.0);
|
||||
}
|
||||
else if (M == rgb.green()) {
|
||||
hue_prime = ((b - r) / chroma) + 2.0;
|
||||
}
|
||||
else if (M == rgb.blue()) {
|
||||
hue_prime = ((r - g) / chroma) + 4.0;
|
||||
}
|
||||
|
||||
h = hue_prime * 60.0;
|
||||
}
|
||||
|
||||
m_hue = h;
|
||||
m_saturation = s;
|
||||
m_value = v;
|
||||
}
|
||||
|
||||
int Hsv::hueInt() const
|
||||
{
|
||||
return int(std::floor(m_hue + 0.5));
|
||||
}
|
||||
|
||||
int Hsv::saturationInt() const
|
||||
{
|
||||
return int(std::floor(m_saturation*100.0 + 0.5));
|
||||
}
|
||||
|
||||
int Hsv::valueInt() const
|
||||
{
|
||||
return int(std::floor(m_value*100.0 + 0.5));
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,81 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_HSV_H_INCLUDED
|
||||
#define GFX_HSV_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/base.h" // MID
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Rgb;
|
||||
|
||||
class Hsv {
|
||||
public:
|
||||
Hsv()
|
||||
: m_hue(0.0)
|
||||
, m_saturation(0.0)
|
||||
, m_value(0.0)
|
||||
{ }
|
||||
|
||||
Hsv(double hue, double saturation, double value);
|
||||
|
||||
Hsv(const Hsv& hsv)
|
||||
: m_hue(hsv.hue())
|
||||
, m_saturation(hsv.saturation())
|
||||
, m_value(hsv.value())
|
||||
{ }
|
||||
|
||||
// RGB to HSV conversion
|
||||
explicit Hsv(const Rgb& rgb);
|
||||
|
||||
// Returns color's hue, a value from 0 to 360
|
||||
double hue() const { return m_hue; }
|
||||
|
||||
// Returns color's saturation, a value from 0 to 100
|
||||
double saturation() const { return m_saturation; }
|
||||
|
||||
// Returns color's brightness, a value from 0 to 100
|
||||
double value() const { return m_value; }
|
||||
|
||||
// Integer getters, hue=[0,360), saturation=[0,100], value=[0,100]
|
||||
int hueInt() const;
|
||||
int saturationInt() const;
|
||||
int valueInt() const;
|
||||
|
||||
void hue(double hue) {
|
||||
m_hue = MID(0.0, hue, 360.0);
|
||||
}
|
||||
|
||||
void saturation(double saturation) {
|
||||
m_saturation = MID(0.0, saturation, 1.0);
|
||||
}
|
||||
|
||||
void value(double value) {
|
||||
m_value = MID(0.0, value, 1.0);
|
||||
}
|
||||
|
||||
// The comparison is done through the integer value of each component.
|
||||
bool operator==(const Hsv& other) const {
|
||||
return (hueInt() == other.hueInt() &&
|
||||
saturationInt() == other.saturationInt() &&
|
||||
valueInt() == other.valueInt());
|
||||
}
|
||||
|
||||
bool operator!=(const Hsv& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_hue;
|
||||
double m_saturation;
|
||||
double m_value;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,66 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2013 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
ostream& operator<<(ostream& os, const Hsv& hsv)
|
||||
{
|
||||
return os << "("
|
||||
<< hsv.hueInt() << ", "
|
||||
<< hsv.saturationInt() << ", "
|
||||
<< hsv.valueInt() << "); real: ("
|
||||
<< hsv.hue() << ", "
|
||||
<< hsv.saturation() << ", "
|
||||
<< hsv.value() << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(Hsv, Ctor)
|
||||
{
|
||||
EXPECT_EQ(35.0, Hsv(35.0, 0.50, 0.75).hue());
|
||||
EXPECT_EQ(0.50, Hsv(35.0, 0.50, 0.75).saturation());
|
||||
EXPECT_EQ(0.75, Hsv(35.0, 0.50, 0.75).value());
|
||||
EXPECT_EQ(35, Hsv(35.0, 0.50, 0.75).hueInt());
|
||||
EXPECT_EQ(50, Hsv(35.0, 0.50, 0.75).saturationInt());
|
||||
EXPECT_EQ(75, Hsv(35.0, 0.50, 0.75).valueInt());
|
||||
EXPECT_EQ(Hsv(0, 0, 0), Hsv());
|
||||
}
|
||||
|
||||
TEST(Hsv, FromRgb)
|
||||
{
|
||||
EXPECT_EQ(Hsv( 0.0, 0.00, 0.00), Hsv(Rgb( 0, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.00, 0.01), Hsv(Rgb( 3, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.00, 0.99), Hsv(Rgb(252, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.00, 1.00), Hsv(Rgb(255, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 60.0, 1.00, 0.75), Hsv(Rgb(191, 191, 0)));
|
||||
EXPECT_EQ(Hsv(120.0, 1.00, 0.50), Hsv(Rgb( 0, 128, 0)));
|
||||
EXPECT_EQ(Hsv(120.0, 1.00, 1.00), Hsv(Rgb( 0, 255, 0)));
|
||||
EXPECT_EQ(Hsv(180.0, 0.50, 1.00), Hsv(Rgb(128, 255, 255)));
|
||||
EXPECT_EQ(Hsv(240.0, 0.50, 1.00), Hsv(Rgb(128, 128, 255)));
|
||||
EXPECT_EQ(Hsv(240.0, 1.00, 1.00), Hsv(Rgb( 0, 0, 255)));
|
||||
EXPECT_EQ(Hsv(300.0, 0.66, 0.75), Hsv(Rgb(191, 64, 191)));
|
||||
EXPECT_EQ(Hsv(360.0, 1.00, 0.99), Hsv(Rgb(252, 0, 0)));
|
||||
EXPECT_EQ(Hsv(360.0, 1.00, 1.00), Hsv(Rgb(255, 0, 0)));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/packing_rects.h"
|
||||
|
||||
#include "gfx/region.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
void PackingRects::add(const Size& sz)
|
||||
{
|
||||
m_rects.push_back(Rect(sz));
|
||||
}
|
||||
|
||||
void PackingRects::add(const Rect& rc)
|
||||
{
|
||||
m_rects.push_back(rc);
|
||||
}
|
||||
|
||||
Size PackingRects::bestFit()
|
||||
{
|
||||
Size size(0, 0);
|
||||
|
||||
// Calculate the amount of pixels that we need, the texture cannot
|
||||
// be smaller than that.
|
||||
int neededArea = 0;
|
||||
for (const auto& rc : m_rects) {
|
||||
neededArea += rc.w * rc.h;
|
||||
}
|
||||
|
||||
int w = 1;
|
||||
int h = 1;
|
||||
int z = 0;
|
||||
bool fit = false;
|
||||
while (true) {
|
||||
if (w*h >= neededArea) {
|
||||
fit = pack(Size(w, h));
|
||||
if (fit) {
|
||||
size = Size(w, h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((++z) & 1)
|
||||
w *= 2;
|
||||
else
|
||||
h *= 2;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool by_area(const Rect* a, const Rect* b) {
|
||||
return a->w*a->h > b->w*b->h;
|
||||
}
|
||||
|
||||
bool PackingRects::pack(const Size& size)
|
||||
{
|
||||
m_bounds = Rect(size);
|
||||
|
||||
// We cannot sort m_rects because we want to
|
||||
std::vector<Rect*> rectPtrs(m_rects.size());
|
||||
int i = 0;
|
||||
for (auto& rc : m_rects)
|
||||
rectPtrs[i++] = &rc;
|
||||
std::sort(rectPtrs.begin(), rectPtrs.end(), by_area);
|
||||
|
||||
gfx::Region rgn(m_bounds);
|
||||
for (auto rcPtr : rectPtrs) {
|
||||
gfx::Rect& rc = *rcPtr;
|
||||
|
||||
for (int v=0; v<=m_bounds.h-rc.h; ++v) {
|
||||
for (int u=0; u<=m_bounds.w-rc.w; ++u) {
|
||||
gfx::Rect possible(u, v, rc.w, rc.h);
|
||||
Region::Overlap overlap = rgn.contains(possible);
|
||||
if (overlap == Region::In) {
|
||||
rc = possible;
|
||||
rgn.createSubtraction(rgn, gfx::Region(rc));
|
||||
goto next_rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // There is not enough room for "rc"
|
||||
next_rc:;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,53 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_TEXTURE_SIZE_H_INCLUDED
|
||||
#define GFX_TEXTURE_SIZE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/fwd.h"
|
||||
#include "gfx/rect.h"
|
||||
#include <vector>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
// TODO add support for rotations
|
||||
class PackingRects {
|
||||
public:
|
||||
typedef std::vector<Rect> Rects;
|
||||
typedef Rects::const_iterator const_iterator;
|
||||
|
||||
// Iterate over all given rectangles (in the same order they where
|
||||
// given in addSize() calls).
|
||||
const_iterator begin() const { return m_rects.begin(); }
|
||||
const_iterator end() const { return m_rects.end(); }
|
||||
|
||||
std::size_t size() const { return m_rects.size(); }
|
||||
const Rect& operator[](int i) const { return m_rects[i]; }
|
||||
|
||||
// Adds a new rectangle.
|
||||
void add(const Size& sz);
|
||||
void add(const Rect& rc);
|
||||
|
||||
// Returns the best size for the texture.
|
||||
Size bestFit();
|
||||
|
||||
// Rearrange all given rectangles to best fit a texture size.
|
||||
// Returns true if all rectangles were correctly arranged or false
|
||||
// if there is not enough space.
|
||||
bool pack(const Size& size);
|
||||
|
||||
// Returns the bounds of the packed area.
|
||||
const Rect& bounds() const { return m_bounds; }
|
||||
|
||||
private:
|
||||
Rect m_bounds;
|
||||
Rects m_rects;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,100 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/packing_rects.h"
|
||||
#include "gfx/rect_io.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
TEST(PackingRects, Simple)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(256, 128));
|
||||
EXPECT_FALSE(pr.pack(Size(256, 120)));
|
||||
EXPECT_TRUE(pr.pack(Size(256, 128)));
|
||||
|
||||
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
|
||||
EXPECT_EQ(Rect(0, 0, 256, 128), pr.bounds());
|
||||
}
|
||||
|
||||
TEST(PackingRects, SimpleTwoRects)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(256, 128));
|
||||
pr.add(Size(256, 120));
|
||||
EXPECT_TRUE(pr.pack(Size(256, 256)));
|
||||
|
||||
EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
|
||||
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
|
||||
EXPECT_EQ(Rect(0, 128, 256, 120), pr[1]);
|
||||
}
|
||||
|
||||
TEST(PackingRects, BestFit)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(10, 12));
|
||||
pr.bestFit();
|
||||
EXPECT_EQ(Rect(0, 0, 16, 16), pr.bounds());
|
||||
}
|
||||
|
||||
TEST(PackingRects, BestFitTwoRects)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(256, 128));
|
||||
pr.add(Size(256, 127));
|
||||
pr.bestFit();
|
||||
|
||||
EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
|
||||
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
|
||||
EXPECT_EQ(Rect(0, 128, 256, 127), pr[1]);
|
||||
}
|
||||
|
||||
TEST(PackingRects, BestFit6Frames100x100)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(100, 100));
|
||||
pr.add(Size(100, 100));
|
||||
pr.add(Size(100, 100));
|
||||
pr.add(Size(100, 100));
|
||||
pr.add(Size(100, 100));
|
||||
pr.add(Size(100, 100));
|
||||
pr.bestFit();
|
||||
|
||||
EXPECT_EQ(Rect(0, 0, 512, 256), pr.bounds());
|
||||
EXPECT_EQ(Rect(0, 0, 100, 100), pr[0]);
|
||||
EXPECT_EQ(Rect(100, 0, 100, 100), pr[1]);
|
||||
EXPECT_EQ(Rect(200, 0, 100, 100), pr[2]);
|
||||
EXPECT_EQ(Rect(300, 0, 100, 100), pr[3]);
|
||||
EXPECT_EQ(Rect(400, 0, 100, 100), pr[4]);
|
||||
EXPECT_EQ(Rect(0, 100, 100, 100), pr[5]);
|
||||
}
|
||||
|
||||
TEST(PackingRects, KeepSameRectsOrder)
|
||||
{
|
||||
PackingRects pr;
|
||||
pr.add(Size(10, 10));
|
||||
pr.add(Size(20, 20));
|
||||
pr.add(Size(30, 30));
|
||||
pr.bestFit();
|
||||
|
||||
EXPECT_EQ(Rect(0, 0, 64, 32), pr.bounds());
|
||||
EXPECT_EQ(Rect(50, 0, 10, 10), pr[0]);
|
||||
EXPECT_EQ(Rect(30, 0, 20, 20), pr[1]);
|
||||
EXPECT_EQ(Rect(0, 0, 30, 30), pr[2]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
125
src/gfx/point.h
125
src/gfx/point.h
@ -1,125 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_POINT_H_INCLUDED
|
||||
#define GFX_POINT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T>
|
||||
class SizeT;
|
||||
|
||||
// A 2D coordinate in the screen.
|
||||
template<typename T>
|
||||
class PointT
|
||||
{
|
||||
public:
|
||||
T x, y;
|
||||
|
||||
PointT() : x(0), y(0) {
|
||||
}
|
||||
|
||||
PointT(const T& x, const T& y) : x(x), y(y) {
|
||||
}
|
||||
|
||||
PointT(const PointT& point) : x(point.x), y(point.y) {
|
||||
}
|
||||
|
||||
template<typename T2>
|
||||
explicit PointT(const PointT<T2>& point) : x(static_cast<T>(point.x)),
|
||||
y(static_cast<T>(point.y)) {
|
||||
}
|
||||
|
||||
explicit PointT(const SizeT<T>& size) : x(size.w), y(size.h) {
|
||||
}
|
||||
|
||||
const PointT& operator=(const PointT& pt) {
|
||||
x = pt.x;
|
||||
y = pt.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator+=(const PointT& pt) {
|
||||
x += pt.x;
|
||||
y += pt.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator-=(const PointT& pt) {
|
||||
x -= pt.x;
|
||||
y -= pt.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator+=(const T& value) {
|
||||
x += value;
|
||||
y += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator-=(const T& value) {
|
||||
x -= value;
|
||||
y -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator*=(const T& value) {
|
||||
x *= value;
|
||||
y *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PointT& operator/=(const T& value) {
|
||||
x /= value;
|
||||
y /= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PointT operator+(const PointT& pt) const {
|
||||
return PointT(x+pt.x, y+pt.y);
|
||||
}
|
||||
|
||||
PointT operator-(const PointT& pt) const {
|
||||
return PointT(x-pt.x, y-pt.y);
|
||||
}
|
||||
|
||||
PointT operator+(const T& value) const {
|
||||
return PointT(x+value, y+value);
|
||||
}
|
||||
|
||||
PointT operator-(const T& value) const {
|
||||
return PointT(x-value, y-value);
|
||||
}
|
||||
|
||||
PointT operator*(const T& value) const {
|
||||
return PointT(x*value, y*value);
|
||||
}
|
||||
|
||||
PointT operator/(const T& value) const {
|
||||
return PointT(x/value, y/value);
|
||||
}
|
||||
|
||||
PointT operator-() const {
|
||||
return PointT(-x, -y);
|
||||
}
|
||||
|
||||
bool operator==(const PointT& pt) const {
|
||||
return x == pt.x && y == pt.y;
|
||||
}
|
||||
|
||||
bool operator!=(const PointT& pt) const {
|
||||
return x != pt.x || y != pt.y;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef PointT<int> Point;
|
||||
typedef PointT<double> PointF;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
365
src/gfx/rect.h
365
src/gfx/rect.h
@ -1,365 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_RECT_H_INCLUDED
|
||||
#define GFX_RECT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T> class PointT;
|
||||
template<typename T> class SizeT;
|
||||
template<typename T> class BorderT;
|
||||
|
||||
// A rectangle.
|
||||
template<typename T>
|
||||
class RectT
|
||||
{
|
||||
public:
|
||||
T x, y, w, h;
|
||||
|
||||
T x2() const { return x+w; }
|
||||
T y2() const { return y+h; }
|
||||
|
||||
// Creates a new empty rectangle with the origin in 0,0.
|
||||
RectT() : x(0), y(0), w(0), h(0) {
|
||||
}
|
||||
|
||||
// Creates a new rectangle with the specified size with the origin in 0,0.
|
||||
RectT(const T& w, const T& h) :
|
||||
x(0), y(0),
|
||||
w(w), h(h) {
|
||||
}
|
||||
|
||||
// Creates a new rectangle with the specified size with the origin in 0,0.
|
||||
explicit RectT(const SizeT<T>& size) :
|
||||
x(0), y(0),
|
||||
w(size.w), h(size.h) {
|
||||
}
|
||||
|
||||
RectT(const RectT<T>& rect) :
|
||||
x(rect.x), y(rect.y),
|
||||
w(rect.w), h(rect.h) {
|
||||
}
|
||||
|
||||
template<typename T2>
|
||||
RectT(const RectT<T2>& rect) :
|
||||
x(static_cast<T>(rect.x)), y(static_cast<T>(rect.y)),
|
||||
w(static_cast<T>(rect.w)), h(static_cast<T>(rect.h)) {
|
||||
}
|
||||
|
||||
RectT(const PointT<T>& point, const SizeT<T>& size) :
|
||||
x(point.x), y(point.y),
|
||||
w(size.w), h(size.h) {
|
||||
}
|
||||
|
||||
// Creates a new rectangle with the origin in point1 and size
|
||||
// equal to point2-point1.
|
||||
//
|
||||
// If a coordinate of point1 is greater than point2, the coordinates
|
||||
// are swapped. The resulting rectangle will be:
|
||||
//
|
||||
// x = min(point1.x, point2.x)
|
||||
// y = min(point1.y, point2.y)
|
||||
// w = max(point1.x, point2.x) - x
|
||||
// h = max(point1.x, point2.x) - y
|
||||
//
|
||||
// See that point2 isn't included in the rectangle, it's like the
|
||||
// point returned by point2() member function.
|
||||
RectT(const PointT<T>& point1, const PointT<T>& point2) {
|
||||
PointT<T> leftTop = point1;
|
||||
PointT<T> rightBottom = point2;
|
||||
T t;
|
||||
|
||||
if (leftTop.x > rightBottom.x) {
|
||||
t = leftTop.x;
|
||||
leftTop.x = rightBottom.x;
|
||||
rightBottom.x = t;
|
||||
}
|
||||
|
||||
if (leftTop.y > rightBottom.y) {
|
||||
t = leftTop.y;
|
||||
leftTop.y = rightBottom.y;
|
||||
rightBottom.y = t;
|
||||
}
|
||||
|
||||
this->x = leftTop.x;
|
||||
this->y = leftTop.y;
|
||||
this->w = rightBottom.x - leftTop.x;
|
||||
this->h = rightBottom.y - leftTop.y;
|
||||
}
|
||||
|
||||
RectT(const T& x, const T& y, const T& w, const T& h) : x(x), y(y), w(w), h(h) {
|
||||
}
|
||||
|
||||
// Verifies if the width and/or height of the rectangle are less or
|
||||
// equal than zero.
|
||||
bool isEmpty() const {
|
||||
return (w <= 0 || h <= 0);
|
||||
}
|
||||
|
||||
// Returns the middle point of the rectangle (x+w/2, y+h/2).
|
||||
PointT<T> center() const {
|
||||
return PointT<T>(x+w/2, y+h/2);
|
||||
}
|
||||
|
||||
// Returns the point in the upper-left corner (that is inside the
|
||||
// rectangle).
|
||||
PointT<T> origin() const {
|
||||
return PointT<T>(x, y);
|
||||
}
|
||||
|
||||
// Returns point in the lower-right corner that is outside the
|
||||
// rectangle (x+w, y+h).
|
||||
PointT<T> point2() const {
|
||||
return PointT<T>(x+w, y+h);
|
||||
}
|
||||
|
||||
SizeT<T> size() const {
|
||||
return SizeT<T>(w, h);
|
||||
}
|
||||
|
||||
RectT& setOrigin(const PointT<T>& pt) {
|
||||
x = pt.x;
|
||||
y = pt.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& setSize(const SizeT<T>& sz) {
|
||||
w = sz.w;
|
||||
h = sz.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Moves the rectangle origin in the specified delta.
|
||||
RectT& offset(const T& dx, const T& dy) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& offset(const PointT<T>& delta) {
|
||||
x += delta.x;
|
||||
y += delta.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& inflate(const T& delta) {
|
||||
w += delta;
|
||||
h += delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& inflate(const T& dw, const T& dh) {
|
||||
w += dw;
|
||||
h += dh;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& inflate(const SizeT<T>& delta) {
|
||||
w += delta.w;
|
||||
h += delta.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& enlarge(const T& unit) {
|
||||
x -= unit;
|
||||
y -= unit;
|
||||
w += unit<<1;
|
||||
h += unit<<1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& enlarge(const BorderT<T>& br) {
|
||||
x -= br.left();
|
||||
y -= br.top();
|
||||
w += br.left() + br.right();
|
||||
h += br.top() + br.bottom();
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& enlargeXW(const T& unit) {
|
||||
x -= unit;
|
||||
w += unit<<1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& enlargeYH(const T& unit) {
|
||||
y -= unit;
|
||||
h += unit<<1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& shrink(const T& unit) {
|
||||
x += unit;
|
||||
y += unit;
|
||||
w -= unit<<1;
|
||||
h -= unit<<1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& shrink(const BorderT<T>& br) {
|
||||
x += br.left();
|
||||
y += br.top();
|
||||
w -= br.left() + br.right();
|
||||
h -= br.top() + br.bottom();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns true if this rectangle encloses the pt point.
|
||||
bool contains(const PointT<T>& pt) const {
|
||||
return
|
||||
pt.x >= x && pt.x < x+w &&
|
||||
pt.y >= y && pt.y < y+h;
|
||||
}
|
||||
|
||||
bool contains(const T& u, const T& v) const {
|
||||
return
|
||||
u >= x && u < x+w &&
|
||||
v >= y && v < y+h;
|
||||
}
|
||||
|
||||
// Returns true if this rectangle entirely contains the rc rectangle.
|
||||
bool contains(const RectT& rc) const {
|
||||
if (isEmpty() || rc.isEmpty())
|
||||
return false;
|
||||
|
||||
return
|
||||
rc.x >= x && rc.x+rc.w <= x+w &&
|
||||
rc.y >= y && rc.y+rc.h <= y+h;
|
||||
}
|
||||
|
||||
// Returns true if the intersection between this rectangle with rc
|
||||
// rectangle is not empty.
|
||||
bool intersects(const RectT& rc) const {
|
||||
if (isEmpty() || rc.isEmpty())
|
||||
return false;
|
||||
|
||||
return
|
||||
rc.x < x+w && rc.x+rc.w > x &&
|
||||
rc.y < y+h && rc.y+rc.h > y;
|
||||
}
|
||||
|
||||
// Returns the union rectangle between this and rc rectangle.
|
||||
RectT createUnion(const RectT& rc) const {
|
||||
if (isEmpty())
|
||||
return rc;
|
||||
else if (rc.isEmpty())
|
||||
return *this;
|
||||
else
|
||||
return RectT(PointT<T>(x < rc.x ? x: rc.x,
|
||||
y < rc.y ? y: rc.y),
|
||||
PointT<T>(x+w > rc.x+rc.w ? x+w: rc.x+rc.w,
|
||||
y+h > rc.y+rc.h ? y+h: rc.y+rc.h));
|
||||
}
|
||||
|
||||
// Returns the intersection rectangle between this and rc rectangles.
|
||||
RectT createIntersection(const RectT& rc) const {
|
||||
if (intersects(rc))
|
||||
return RectT(PointT<T>(x > rc.x ? x: rc.x,
|
||||
y > rc.y ? y: rc.y),
|
||||
PointT<T>(x+w < rc.x+rc.w ? x+w: rc.x+rc.w,
|
||||
y+h < rc.y+rc.h ? y+h: rc.y+rc.h));
|
||||
else
|
||||
return RectT();
|
||||
}
|
||||
|
||||
const RectT& operator+=(const BorderT<T>& br) {
|
||||
enlarge(br);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const RectT& operator-=(const BorderT<T>& br) {
|
||||
shrink(br);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& operator*=(const int factor) {
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
w *= factor;
|
||||
h *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& operator/=(const int factor) {
|
||||
x /= factor;
|
||||
y /= factor;
|
||||
w /= factor;
|
||||
h /= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& operator*=(const SizeT<T>& size) {
|
||||
x *= size.w;
|
||||
y *= size.h;
|
||||
w *= size.w;
|
||||
h *= size.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& operator/=(const SizeT<T>& size) {
|
||||
x /= size.w;
|
||||
y /= size.h;
|
||||
w /= size.w;
|
||||
h /= size.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const RectT& operator|=(const RectT& rc) {
|
||||
return *this = createUnion(rc);
|
||||
}
|
||||
|
||||
const RectT& operator&=(const RectT& rc) {
|
||||
return *this = createIntersection(rc);
|
||||
}
|
||||
|
||||
RectT operator+(const BorderT<T>& br) const {
|
||||
return RectT(*this).enlarge(br);
|
||||
}
|
||||
|
||||
RectT operator-(const BorderT<T>& br) const {
|
||||
return RectT(*this).shrink(br);
|
||||
}
|
||||
|
||||
RectT operator|(const RectT& other) const {
|
||||
return createUnion(other);
|
||||
}
|
||||
|
||||
RectT operator&(const RectT& other) const {
|
||||
return createIntersection(other);
|
||||
}
|
||||
|
||||
RectT operator*(const SizeT<T>& size) const {
|
||||
return RectT(x*size.w, y*size.h,
|
||||
w*size.w, h*size.h);
|
||||
}
|
||||
|
||||
RectT operator/(const SizeT<T>& size) const {
|
||||
return RectT(x/size.w, y/size.h,
|
||||
w/size.w, h/size.h);
|
||||
}
|
||||
|
||||
bool operator==(const RectT& rc) const {
|
||||
return
|
||||
x == rc.x && w == rc.w &&
|
||||
y == rc.y && h == rc.h;
|
||||
}
|
||||
|
||||
bool operator!=(const RectT& rc) const {
|
||||
return
|
||||
x != rc.x || w != rc.w ||
|
||||
y != rc.y || h != rc.h;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef RectT<int> Rect;
|
||||
typedef RectT<double> RectF;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,42 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_RECT_IO_H_INCLUDED
|
||||
#define GFX_RECT_IO_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/rect.h"
|
||||
#include <iosfwd>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const Rect& rect) {
|
||||
return os << "("
|
||||
<< rect.x << ", "
|
||||
<< rect.y << ", "
|
||||
<< rect.w << ", "
|
||||
<< rect.h << ")";
|
||||
}
|
||||
|
||||
inline std::istream& operator>>(std::istream& in, Rect& rect) {
|
||||
while (in && in.get() != '(')
|
||||
;
|
||||
|
||||
if (!in)
|
||||
return in;
|
||||
|
||||
char chr;
|
||||
in >> rect.x >> chr
|
||||
>> rect.y >> chr
|
||||
>> rect.w >> chr
|
||||
>> rect.h >> chr;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,54 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2013 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/border.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/rect_io.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gfx;
|
||||
|
||||
TEST(Rect, Ctor)
|
||||
{
|
||||
EXPECT_EQ(Rect(0, 0, 0, 0), Rect());
|
||||
EXPECT_EQ(10, Rect(10, 20, 30, 40).x);
|
||||
EXPECT_EQ(20, Rect(10, 20, 30, 40).y);
|
||||
EXPECT_EQ(30, Rect(10, 20, 30, 40).w);
|
||||
EXPECT_EQ(40, Rect(10, 20, 30, 40).h);
|
||||
}
|
||||
|
||||
TEST(Rect, Inflate)
|
||||
{
|
||||
EXPECT_EQ(Rect(10, 20, 31, 42), Rect(10, 20, 30, 40).inflate(1, 2));
|
||||
EXPECT_EQ(Rect(10, 20, 31, 42), Rect(10, 20, 30, 40).inflate(Size(1, 2)));
|
||||
}
|
||||
|
||||
TEST(Rect, Enlarge)
|
||||
{
|
||||
EXPECT_EQ(Rect(9, 19, 32, 42), Rect(10, 20, 30, 40).enlarge(1));
|
||||
EXPECT_EQ(Rect(9, 18, 34, 46), Rect(10, 20, 30, 40).enlarge(Border(1, 2, 3, 4)));
|
||||
EXPECT_EQ(Rect(9, 18, 34, 46), Rect(10, 20, 30, 40) + Border(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(Rect, Shrink)
|
||||
{
|
||||
EXPECT_EQ(Rect(11, 21, 28, 38), Rect(10, 20, 30, 40).shrink(1));
|
||||
EXPECT_EQ(Rect(11, 22, 26, 34), Rect(10, 20, 30, 40).shrink(Border(1, 2, 3, 4)));
|
||||
EXPECT_EQ(Rect(11, 22, 26, 34), Rect(10, 20, 30, 40) - Border(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2013, 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "pixman.h"
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
inline Rect to_rect(const pixman_box32& extends)
|
||||
{
|
||||
return Rect(
|
||||
extends.x1, extends.y1,
|
||||
extends.x2 - extends.x1,
|
||||
extends.y2 - extends.y1);
|
||||
}
|
||||
|
||||
Region::Region()
|
||||
{
|
||||
pixman_region32_init(&m_region);
|
||||
}
|
||||
|
||||
Region::Region(const Region& copy)
|
||||
{
|
||||
pixman_region32_init(&m_region);
|
||||
pixman_region32_copy(&m_region, ©.m_region);
|
||||
}
|
||||
|
||||
Region::Region(const Rect& rect)
|
||||
{
|
||||
if (!rect.isEmpty())
|
||||
pixman_region32_init_rect(&m_region, rect.x, rect.y, rect.w, rect.h);
|
||||
else
|
||||
pixman_region32_init(&m_region);
|
||||
}
|
||||
|
||||
Region::~Region()
|
||||
{
|
||||
pixman_region32_fini(&m_region);
|
||||
}
|
||||
|
||||
Region& Region::operator=(const Rect& rect)
|
||||
{
|
||||
if (!rect.isEmpty()) {
|
||||
pixman_box32 box = { rect.x, rect.y, rect.x2(), rect.y2() };
|
||||
pixman_region32_reset(&m_region, &box);
|
||||
}
|
||||
else
|
||||
pixman_region32_clear(&m_region);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Region& Region::operator=(const Region& copy)
|
||||
{
|
||||
pixman_region32_copy(&m_region, ©.m_region);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Region::iterator Region::begin()
|
||||
{
|
||||
iterator it;
|
||||
it.m_ptr = pixman_region32_rectangles(&m_region, NULL);
|
||||
return it;
|
||||
}
|
||||
|
||||
Region::iterator Region::end()
|
||||
{
|
||||
iterator it;
|
||||
it.m_ptr = pixman_region32_rectangles(&m_region, NULL) + size();
|
||||
return it;
|
||||
}
|
||||
|
||||
Region::const_iterator Region::begin() const
|
||||
{
|
||||
const_iterator it;
|
||||
it.m_ptr = pixman_region32_rectangles(&m_region, NULL);
|
||||
return it;
|
||||
}
|
||||
|
||||
Region::const_iterator Region::end() const
|
||||
{
|
||||
const_iterator it;
|
||||
it.m_ptr = pixman_region32_rectangles(&m_region, NULL) + size();
|
||||
return it;
|
||||
}
|
||||
|
||||
bool Region::isEmpty() const
|
||||
{
|
||||
return pixman_region32_not_empty(&m_region) ? false: true;
|
||||
}
|
||||
|
||||
Rect Region::bounds() const
|
||||
{
|
||||
return to_rect(*pixman_region32_extents(&m_region));
|
||||
}
|
||||
|
||||
std::size_t Region::size() const
|
||||
{
|
||||
return pixman_region32_n_rects(&m_region);
|
||||
}
|
||||
|
||||
void Region::clear()
|
||||
{
|
||||
pixman_region32_clear(&m_region);
|
||||
}
|
||||
|
||||
void Region::offset(int dx, int dy)
|
||||
{
|
||||
pixman_region32_translate(&m_region, dx, dy);
|
||||
}
|
||||
|
||||
void Region::offset(const PointT<int>& delta)
|
||||
{
|
||||
pixman_region32_translate(&m_region, delta.x, delta.y);
|
||||
}
|
||||
|
||||
Region& Region::createIntersection(const Region& a, const Region& b)
|
||||
{
|
||||
pixman_region32_intersect(&m_region, &a.m_region, &b.m_region);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Region& Region::createUnion(const Region& a, const Region& b)
|
||||
{
|
||||
pixman_region32_union(&m_region, &a.m_region, &b.m_region);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Region& Region::createSubtraction(const Region& a, const Region& b)
|
||||
{
|
||||
pixman_region32_subtract(&m_region, &a.m_region, &b.m_region);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Region::contains(const PointT<int>& pt) const
|
||||
{
|
||||
return pixman_region32_contains_point(&m_region, pt.x, pt.y, NULL) ? true: false;
|
||||
}
|
||||
|
||||
Region::Overlap Region::contains(const Rect& rect) const
|
||||
{
|
||||
static_assert(
|
||||
int(Out) == int(PIXMAN_REGION_OUT) &&
|
||||
int(In) == int(PIXMAN_REGION_IN) &&
|
||||
int(Part) == int(PIXMAN_REGION_PART), "Pixman constants have changed");
|
||||
|
||||
pixman_box32 box = { rect.x, rect.y, rect.x2(), rect.y2() };
|
||||
return (Region::Overlap)pixman_region32_contains_rectangle(&m_region, &box);
|
||||
}
|
||||
|
||||
Rect Region::operator[](int i)
|
||||
{
|
||||
assert(i >= 0 && i < (int)size());
|
||||
return to_rect(pixman_region32_rectangles(&m_region, NULL)[i]);
|
||||
}
|
||||
|
||||
const Rect Region::operator[](int i) const
|
||||
{
|
||||
assert(i >= 0 && i < (int)size());
|
||||
return to_rect(pixman_region32_rectangles(&m_region, NULL)[i]);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
115
src/gfx/region.h
115
src/gfx/region.h
@ -1,115 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_REGION_H_INCLUDED
|
||||
#define GFX_REGION_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/rect.h"
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T> class PointT;
|
||||
|
||||
class Region;
|
||||
|
||||
namespace details {
|
||||
|
||||
#ifdef PIXMAN_VERSION_MAJOR
|
||||
typedef struct pixman_box32 Box;
|
||||
typedef struct pixman_region32 Region;
|
||||
#else
|
||||
struct Box {
|
||||
int32_t x1, y1, x2, y2;
|
||||
};
|
||||
struct Region {
|
||||
Box extents;
|
||||
void* data;
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
class RegionIterator : public std::iterator<std::forward_iterator_tag, T> {
|
||||
public:
|
||||
typedef typename std::iterator<std::forward_iterator_tag, T>::reference reference;
|
||||
|
||||
RegionIterator() : m_ptr(NULL) { }
|
||||
RegionIterator(const RegionIterator& o) : m_ptr(o.m_ptr) { }
|
||||
template<typename T2>
|
||||
RegionIterator(const RegionIterator<T2>& o) : m_ptr(o.m_ptr) { }
|
||||
RegionIterator& operator=(const RegionIterator& o) { m_ptr = o.m_ptr; return *this; }
|
||||
RegionIterator& operator++() { ++m_ptr; return *this; }
|
||||
RegionIterator operator++(int) { RegionIterator o(*this); ++m_ptr; return o; }
|
||||
bool operator==(const RegionIterator& o) const { return m_ptr == o.m_ptr; }
|
||||
bool operator!=(const RegionIterator& o) const { return m_ptr != o.m_ptr; }
|
||||
reference operator*() {
|
||||
m_rect.x = m_ptr->x1;
|
||||
m_rect.y = m_ptr->y1;
|
||||
m_rect.w = m_ptr->x2 - m_ptr->x1;
|
||||
m_rect.h = m_ptr->y2 - m_ptr->y1;
|
||||
return m_rect;
|
||||
}
|
||||
private:
|
||||
Box* m_ptr;
|
||||
mutable Rect m_rect;
|
||||
template<typename> friend class RegionIterator;
|
||||
friend class ::gfx::Region;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
class Region {
|
||||
public:
|
||||
enum Overlap { Out, In, Part };
|
||||
|
||||
typedef details::RegionIterator<Rect> iterator;
|
||||
typedef details::RegionIterator<const Rect> const_iterator;
|
||||
|
||||
Region();
|
||||
Region(const Region& copy);
|
||||
explicit Region(const Rect& rect);
|
||||
Region& operator=(const Rect& rect);
|
||||
Region& operator=(const Region& copy);
|
||||
~Region();
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
bool isEmpty() const;
|
||||
Rect bounds() const;
|
||||
std::size_t size() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void offset(int dx, int dy);
|
||||
void offset(const PointT<int>& delta);
|
||||
|
||||
Region& createIntersection(const Region& a, const Region& b);
|
||||
Region& createUnion(const Region& a, const Region& b);
|
||||
Region& createSubtraction(const Region& a, const Region& b);
|
||||
|
||||
bool contains(const PointT<int>& pt) const;
|
||||
Overlap contains(const Rect& rect) const;
|
||||
|
||||
Rect operator[](int i);
|
||||
const Rect operator[](int i) const;
|
||||
|
||||
Region& operator+=(const Region& b) { return createUnion(*this, b); }
|
||||
Region& operator|=(const Region& b) { return createUnion(*this, b); }
|
||||
Region& operator&=(const Region& b) { return createIntersection(*this, b); }
|
||||
Region& operator-=(const Region& b) { return createSubtraction(*this, b); }
|
||||
|
||||
private:
|
||||
mutable details::Region m_region;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,119 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect_io.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gfx;
|
||||
|
||||
ostream& operator<<(ostream& os, const Region& rgn)
|
||||
{
|
||||
os << "{";
|
||||
for (Region::const_iterator it=rgn.begin(), end=rgn.end();
|
||||
it != end; ) {
|
||||
os << *it;
|
||||
++it;
|
||||
if (it != end)
|
||||
os << ", ";
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
TEST(Region, Ctor)
|
||||
{
|
||||
EXPECT_EQ(0, Region().size());
|
||||
EXPECT_TRUE(Region(Rect(0, 0, 0, 0)).isEmpty());
|
||||
EXPECT_TRUE(Region().isEmpty());
|
||||
ASSERT_EQ(0, Region(Rect(0, 0, 0, 0)).size());
|
||||
ASSERT_EQ(1, Region(Rect(0, 0, 1, 1)).size());
|
||||
EXPECT_EQ(Rect(2, 3, 4, 5), Region(Rect(2, 3, 4, 5))[0]);
|
||||
}
|
||||
|
||||
TEST(Region, Equal)
|
||||
{
|
||||
Region a;
|
||||
a = Rect(2, 3, 4, 5);
|
||||
EXPECT_EQ(Rect(2, 3, 4, 5), a.bounds());
|
||||
EXPECT_EQ(Rect(2, 3, 4, 5), a[0]);
|
||||
EXPECT_FALSE(a.isEmpty());
|
||||
|
||||
a = Rect(6, 7, 8, 9);
|
||||
EXPECT_EQ(Rect(6, 7, 8, 9), a.bounds());
|
||||
EXPECT_EQ(Rect(6, 7, 8, 9), a[0]);
|
||||
|
||||
Region b;
|
||||
b = a;
|
||||
EXPECT_EQ(Rect(6, 7, 8, 9), b[0]);
|
||||
|
||||
b = Rect(0, 0, 0, 0);
|
||||
EXPECT_TRUE(b.isEmpty());
|
||||
}
|
||||
|
||||
TEST(Region, Clear)
|
||||
{
|
||||
Region a(Rect(2, 3, 4, 5));
|
||||
EXPECT_FALSE(a.isEmpty());
|
||||
a.clear();
|
||||
EXPECT_TRUE(a.isEmpty());
|
||||
}
|
||||
|
||||
TEST(Region, Union)
|
||||
{
|
||||
Region a(Rect(2, 3, 4, 5));
|
||||
Region b(Rect(6, 3, 4, 5));
|
||||
EXPECT_EQ(Rect(2, 3, 8, 5), Region().createUnion(a, b).bounds());
|
||||
EXPECT_EQ(Rect(2, 3, 8, 5), Region().createUnion(b, a).bounds());
|
||||
ASSERT_EQ(1, Region().createUnion(a, b).size());
|
||||
ASSERT_EQ(1, Region().createUnion(b, a).size());
|
||||
EXPECT_EQ(Rect(2, 3, 8, 5), Region().createUnion(a, b)[0]);
|
||||
EXPECT_EQ(Rect(2, 3, 8, 5), Region().createUnion(b, a)[0]);
|
||||
}
|
||||
|
||||
TEST(Region, ContainsPoint)
|
||||
{
|
||||
Region a(Rect(2, 3, 4, 5));
|
||||
EXPECT_TRUE(a.contains(Point(2, 3)));
|
||||
EXPECT_FALSE(a.contains(Point(2-1, 3-1)));
|
||||
EXPECT_FALSE(a.contains(Point(2+4, 3)));
|
||||
EXPECT_FALSE(a.contains(Point(2, 3+5)));
|
||||
EXPECT_FALSE(a.contains(Point(2+4, 3+5)));
|
||||
EXPECT_TRUE(a.contains(Point(2+4-1, 3)));
|
||||
EXPECT_TRUE(a.contains(Point(2, 3+5-1)));
|
||||
EXPECT_TRUE(a.contains(Point(2+4-1, 3+5-1)));
|
||||
}
|
||||
|
||||
TEST(Region, Iterators)
|
||||
{
|
||||
Region a;
|
||||
a.createUnion(a, Region(Rect(0, 0, 32, 64)));
|
||||
a.createUnion(a, Region(Rect(0, 0, 64, 32)));
|
||||
int c = 0;
|
||||
for (Region::iterator it=a.begin(), end=a.end(); it!=end; ++it) {
|
||||
++c;
|
||||
}
|
||||
EXPECT_EQ(2, c);
|
||||
|
||||
c = 0;
|
||||
for (Region::const_iterator it=a.begin(), end=a.end(); it!=end; ++it) {
|
||||
++c;
|
||||
}
|
||||
EXPECT_EQ(2, c);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
134
src/gfx/rgb.cpp
134
src/gfx/rgb.cpp
@ -1,134 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "gfx/rgb.h"
|
||||
|
||||
#include "gfx/hsl.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Reference: http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
Rgb::Rgb(const Hsv& hsv)
|
||||
{
|
||||
double chroma = hsv.value() * hsv.saturation();
|
||||
double hue_prime = hsv.hue() / 60.0;
|
||||
double x = chroma * (1.0 - std::fabs(std::fmod(hue_prime, 2.0) - 1.0));
|
||||
double r, g, b;
|
||||
|
||||
r = g = b = 0.0;
|
||||
|
||||
switch (int(hue_prime)) {
|
||||
|
||||
case 6:
|
||||
case 0:
|
||||
r = chroma;
|
||||
g = x;
|
||||
break;
|
||||
case 1:
|
||||
r = x;
|
||||
g = chroma;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
g = chroma;
|
||||
b = x;
|
||||
break;
|
||||
case 3:
|
||||
g = x;
|
||||
b = chroma;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
b = chroma;
|
||||
r = x;
|
||||
break;
|
||||
case 5:
|
||||
b = x;
|
||||
r = chroma;
|
||||
break;
|
||||
}
|
||||
|
||||
double m = hsv.value() - chroma;
|
||||
r += m;
|
||||
g += m;
|
||||
b += m;
|
||||
|
||||
m_red = int(r*255.0+0.5);
|
||||
m_green = int(g*255.0+0.5);
|
||||
m_blue = int(b*255.0+0.5);
|
||||
}
|
||||
|
||||
Rgb::Rgb(const Hsl& hsl)
|
||||
{
|
||||
double chroma = (1.0 - std::fabs(2.0*hsl.lightness() - 1.0)) * hsl.saturation();
|
||||
double hue_prime = hsl.hue() / 60.0;
|
||||
double x = chroma * (1.0 - std::fabs(std::fmod(hue_prime, 2.0) - 1.0));
|
||||
double r, g, b;
|
||||
|
||||
r = g = b = 0.0;
|
||||
|
||||
switch (int(hue_prime)) {
|
||||
|
||||
case 6:
|
||||
case 0:
|
||||
r = chroma;
|
||||
g = x;
|
||||
break;
|
||||
case 1:
|
||||
r = x;
|
||||
g = chroma;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
g = chroma;
|
||||
b = x;
|
||||
break;
|
||||
case 3:
|
||||
g = x;
|
||||
b = chroma;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
b = chroma;
|
||||
r = x;
|
||||
break;
|
||||
case 5:
|
||||
b = x;
|
||||
r = chroma;
|
||||
break;
|
||||
}
|
||||
|
||||
double m = hsl.lightness() - chroma/2.0;
|
||||
r += m;
|
||||
g += m;
|
||||
b += m;
|
||||
|
||||
m_red = int(r*255.0+0.5);
|
||||
m_green = int(g*255.0+0.5);
|
||||
m_blue = int(b*255.0+0.5);
|
||||
}
|
||||
|
||||
int Rgb::maxComponent() const
|
||||
{
|
||||
if (m_red > m_green)
|
||||
return (m_red > m_blue) ? m_red: m_blue;
|
||||
else
|
||||
return (m_green > m_blue) ? m_green: m_blue;
|
||||
}
|
||||
|
||||
int Rgb::minComponent() const
|
||||
{
|
||||
if (m_red < m_green)
|
||||
return (m_red < m_blue) ? m_red: m_blue;
|
||||
else
|
||||
return (m_green < m_blue) ? m_green: m_blue;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
@ -1,94 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_RGB_H_INCLUDED
|
||||
#define GFX_RGB_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Hsv;
|
||||
class Hsl;
|
||||
|
||||
class Rgb {
|
||||
public:
|
||||
Rgb()
|
||||
: m_red(0)
|
||||
, m_green(0)
|
||||
, m_blue(0)
|
||||
{ }
|
||||
|
||||
Rgb(int red, int green, int blue)
|
||||
: m_red(red)
|
||||
, m_green(green)
|
||||
, m_blue(blue)
|
||||
{
|
||||
assert(red >= 0 && red <= 255);
|
||||
assert(green >= 0 && green <= 255);
|
||||
assert(blue >= 0 && blue <= 255);
|
||||
}
|
||||
|
||||
Rgb(const Rgb& rgb)
|
||||
: m_red(rgb.red())
|
||||
, m_green(rgb.green())
|
||||
, m_blue(rgb.blue())
|
||||
{ }
|
||||
|
||||
// Conversions
|
||||
explicit Rgb(const Hsv& hsv);
|
||||
explicit Rgb(const Hsl& hsl);
|
||||
|
||||
int red() const {
|
||||
return m_red;
|
||||
}
|
||||
|
||||
int green() const {
|
||||
return m_green;
|
||||
}
|
||||
|
||||
int blue() const {
|
||||
return m_blue;
|
||||
}
|
||||
|
||||
int maxComponent() const;
|
||||
int minComponent() const;
|
||||
|
||||
void red(int red) {
|
||||
assert(red >= 0 && red <= 255);
|
||||
m_red = red;
|
||||
}
|
||||
|
||||
void green(int green) {
|
||||
assert(green >= 0 && green <= 255);
|
||||
m_green = green;
|
||||
}
|
||||
|
||||
void blue(int blue) {
|
||||
assert(blue >= 0 && blue <= 255);
|
||||
m_blue = blue;
|
||||
}
|
||||
|
||||
bool operator==(const Rgb& other) const {
|
||||
return (m_red == other.m_red &&
|
||||
m_green == other.m_green &&
|
||||
m_blue == other.m_blue);
|
||||
}
|
||||
|
||||
bool operator!=(const Rgb& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_red;
|
||||
int m_green;
|
||||
int m_blue;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,94 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2013 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/rgb.h"
|
||||
#include "gfx/hsv.h"
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
ostream& operator<<(ostream& os, const Rgb& rgb) {
|
||||
return os << "("
|
||||
<< rgb.red() << ", "
|
||||
<< rgb.green() << ", "
|
||||
<< rgb.blue() << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(Rgb, Ctor)
|
||||
{
|
||||
EXPECT_EQ(1, Rgb(1, 2, 3).red());
|
||||
EXPECT_EQ(2, Rgb(1, 2, 3).green());
|
||||
EXPECT_EQ(3, Rgb(1, 2, 3).blue());
|
||||
EXPECT_EQ(Rgb(0, 0, 0), Rgb());
|
||||
}
|
||||
|
||||
TEST(Rgb, Equal)
|
||||
{
|
||||
EXPECT_TRUE(Rgb(1, 2, 3) == Rgb(1, 2, 3));
|
||||
EXPECT_FALSE(Rgb(1, 2, 3) != Rgb(1, 2, 3));
|
||||
EXPECT_FALSE(Rgb(1, 2, 3) == Rgb(1, 3, 2));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(0, 0, 1));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(0, 1, 0));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(Rgb, MaxComponent)
|
||||
{
|
||||
EXPECT_EQ(3, Rgb(1, 2, 3).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(1, 3, 2).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(2, 1, 3).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(2, 3, 1).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(3, 1, 2).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(3, 2, 1).maxComponent());
|
||||
}
|
||||
|
||||
TEST(Rgb, MinComponent)
|
||||
{
|
||||
EXPECT_EQ(1, Rgb(1, 2, 3).minComponent());
|
||||
EXPECT_EQ(1, Rgb(1, 3, 2).minComponent());
|
||||
EXPECT_EQ(1, Rgb(2, 1, 3).minComponent());
|
||||
EXPECT_EQ(1, Rgb(2, 3, 1).minComponent());
|
||||
EXPECT_EQ(1, Rgb(3, 1, 2).minComponent());
|
||||
EXPECT_EQ(1, Rgb(3, 2, 1).minComponent());
|
||||
}
|
||||
|
||||
TEST(Rgb, FromHsv)
|
||||
{
|
||||
for (double hue = 0.0; hue <= 360.0; hue += 10.0) {
|
||||
EXPECT_EQ(Rgb(255, 255, 255), Rgb(Hsv(hue, 0.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 128, 128), Rgb(Hsv(hue, 0.000, 0.500)));
|
||||
EXPECT_EQ(Rgb( 0, 0, 0), Rgb(Hsv(hue, 0.000, 0.000)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(Rgb( 3, 0, 0), Rgb(Hsv( 0.0, 1.000, 0.010)));
|
||||
EXPECT_EQ(Rgb(252, 0, 0), Rgb(Hsv( 0.0, 1.000, 0.990)));
|
||||
EXPECT_EQ(Rgb(255, 0, 0), Rgb(Hsv( 0.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(191, 191, 0), Rgb(Hsv( 60.0, 1.000, 0.750)));
|
||||
EXPECT_EQ(Rgb( 0, 128, 0), Rgb(Hsv(120.0, 1.000, 0.500)));
|
||||
EXPECT_EQ(Rgb( 0, 255, 0), Rgb(Hsv(120.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 255, 255), Rgb(Hsv(180.0, 0.500, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 128, 255), Rgb(Hsv(240.0, 0.500, 1.000)));
|
||||
EXPECT_EQ(Rgb( 0, 0, 255), Rgb(Hsv(240.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(191, 64, 191), Rgb(Hsv(300.0, 0.667, 0.750)));
|
||||
EXPECT_EQ(Rgb(252, 0, 0), Rgb(Hsv(360.0, 1.000, 0.990)));
|
||||
EXPECT_EQ(Rgb(255, 0, 0), Rgb(Hsv(360.0, 1.000, 1.000)));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
145
src/gfx/size.h
145
src/gfx/size.h
@ -1,145 +0,0 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_SIZE_H_INCLUDED
|
||||
#define GFX_SIZE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template<typename T>
|
||||
class PointT;
|
||||
|
||||
// A 2D size.
|
||||
template<typename T>
|
||||
class SizeT
|
||||
{
|
||||
public:
|
||||
T w, h;
|
||||
|
||||
SizeT() : w(0), h(0) {
|
||||
}
|
||||
|
||||
SizeT(const T& w, const T& h) : w(w), h(h) {
|
||||
}
|
||||
|
||||
SizeT(const SizeT& size) : w(size.w), h(size.h) {
|
||||
}
|
||||
|
||||
template<typename T2>
|
||||
explicit SizeT(const SizeT<T2>& size) : w(static_cast<T>(size.w)),
|
||||
h(static_cast<T>(size.h)) {
|
||||
}
|
||||
|
||||
explicit SizeT(const PointT<T>& point) : w(point.x), h(point.y) {
|
||||
}
|
||||
|
||||
SizeT createUnion(const SizeT& sz) const {
|
||||
return SizeT(std::max(w, sz.w),
|
||||
std::max(h, sz.h));
|
||||
}
|
||||
|
||||
SizeT createIntersection(const SizeT& sz) const {
|
||||
return SizeT(std::min(w, sz.w),
|
||||
std::min(h, sz.h));
|
||||
}
|
||||
|
||||
const SizeT& operator=(const SizeT& sz) {
|
||||
w = sz.w;
|
||||
h = sz.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator+=(const SizeT& sz) {
|
||||
w += sz.w;
|
||||
h += sz.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator-=(const SizeT& sz) {
|
||||
w -= sz.w;
|
||||
h -= sz.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator+=(const T& value) {
|
||||
w += value;
|
||||
h += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator-=(const T& value) {
|
||||
w -= value;
|
||||
h -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator*=(const T& value) {
|
||||
w *= value;
|
||||
h *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator/=(const T& value) {
|
||||
w /= value;
|
||||
h /= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SizeT& operator|=(const SizeT& sz) {
|
||||
return *this = createUnion(sz);
|
||||
}
|
||||
|
||||
const SizeT& operator&=(const SizeT& sz) {
|
||||
return *this = createIntersection(sz);
|
||||
}
|
||||
|
||||
SizeT operator+(const SizeT& sz) const {
|
||||
return SizeT(w+sz.w, h+sz.h);
|
||||
}
|
||||
|
||||
SizeT operator-(const SizeT& sz) const {
|
||||
return SizeT(w-sz.w, h-sz.h);
|
||||
}
|
||||
|
||||
SizeT operator+(const T& value) const {
|
||||
return SizeT(w+value, h+value);
|
||||
}
|
||||
|
||||
SizeT operator-(const T& value) const {
|
||||
return SizeT(w-value, h-value);
|
||||
}
|
||||
|
||||
SizeT operator*(const T& value) const {
|
||||
return SizeT(w*value, h*value);
|
||||
}
|
||||
|
||||
SizeT operator/(const T& value) const {
|
||||
return SizeT(w/value, h/value);
|
||||
}
|
||||
|
||||
SizeT operator-() const {
|
||||
return SizeT(-w, -h);
|
||||
}
|
||||
|
||||
bool operator==(const SizeT& sz) const {
|
||||
return w == sz.w && h == sz.h;
|
||||
}
|
||||
|
||||
bool operator!=(const SizeT& sz) const {
|
||||
return w != sz.w || h != sz.h;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef SizeT<int> Size;
|
||||
typedef SizeT<double> SizeF;
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
@ -1,175 +0,0 @@
|
||||
# LAF OS
|
||||
# Copyright (C) 2012-2018 David Capello
|
||||
|
||||
# TODO the following variables should be available through options
|
||||
# in this file instead of the main Aseprite CMakeLists.txt:
|
||||
# - USE_NONE_BACKEND
|
||||
# - USE_SKIA_BACKEND
|
||||
# - WITH_GTK_FILE_DIALOG_SUPPORT
|
||||
|
||||
set(OS_SOURCES
|
||||
common/freetype_font.cpp
|
||||
draw_text.cpp
|
||||
system.cpp)
|
||||
|
||||
######################################################################
|
||||
# Skia backend
|
||||
|
||||
if(USE_SKIA_BACKEND)
|
||||
set(SKIA_DIR "" CACHE PATH "Skia source code directory")
|
||||
|
||||
add_definitions(
|
||||
-DSK_INTERNAL
|
||||
-DSK_GAMMA_SRGB
|
||||
-DSK_GAMMA_APPLY_TO_A8
|
||||
-DSK_SCALAR_TO_FLOAT_EXCLUDED
|
||||
-DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1
|
||||
-DSK_SUPPORT_OPENCL=0
|
||||
-DSK_FORCE_DISTANCE_FIELD_TEXT=0
|
||||
-DGR_GL_FUNCTION_TYPE=__stdcall
|
||||
# TODO change this to 1
|
||||
-DSK_SUPPORT_GPU=0)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DSK_BUILD_FOR_WIN32)
|
||||
elseif(APPLE)
|
||||
add_definitions(-DSK_BUILD_FOR_MAC)
|
||||
add_definitions(-Wno-ignored-attributes -Wno-unused-result)
|
||||
|
||||
# Use Automatic Reference Counting
|
||||
add_definitions(-fobjc-arc)
|
||||
else()
|
||||
add_definitions(-DSK_SAMPLES_FOR_X)
|
||||
endif()
|
||||
|
||||
if(NOT SKIA_DIR)
|
||||
set(SKIA_OUT_DIR "" CACHE PATH "Skia output directory")
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(SKIA_OUT_DIR "${SKIA_DIR}/out/Debug" CACHE PATH "Skia output directory")
|
||||
else()
|
||||
set(SKIA_OUT_DIR "${SKIA_DIR}/out/Release" CACHE PATH "Skia output directory")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_library(SKIA_LIBRARY skia PATH "${SKIA_OUT_DIR}")
|
||||
if(WIN32)
|
||||
find_library(SKIA_OPENGL_LIBRARY opengl32)
|
||||
else()
|
||||
find_library(SKIA_OPENGL_LIBRARY opengl NAMES GL)
|
||||
endif()
|
||||
|
||||
find_path(SKIA_CONFIG_INCLUDE_DIR SkUserConfig.h HINTS "${SKIA_DIR}/include/config")
|
||||
find_path(SKIA_CORE_INCLUDE_DIR SkCanvas.h HINTS "${SKIA_DIR}/include/core")
|
||||
find_path(SKIA_UTILS_INCLUDE_DIR SkRandom.h HINTS "${SKIA_DIR}/include/utils")
|
||||
find_path(SKIA_CODEC_INCLUDE_DIR SkCodec.h HINTS "${SKIA_DIR}/include/codec")
|
||||
find_path(SKIA_EFFECTS_INCLUDE_DIR SkImageSource.h HINTS "${SKIA_DIR}/include/effects")
|
||||
find_path(SKIA_GPU_INCLUDE_DIR GrContext.h HINTS "${SKIA_DIR}/include/gpu")
|
||||
find_path(SKIA_GPU2_INCLUDE_DIR gl/GrGLDefines.h HINTS "${SKIA_DIR}/src/gpu")
|
||||
find_path(SKIA_ANGLE_INCLUDE_DIR angle_gl.h HINTS "${SKIA_DIR}/third_party/externals/angle2/include")
|
||||
|
||||
include_directories(
|
||||
${SKIA_CONFIG_INCLUDE_DIR}
|
||||
${SKIA_CORE_INCLUDE_DIR}
|
||||
${SKIA_PORTS_INCLUDE_DIR}
|
||||
${SKIA_UTILS_INCLUDE_DIR}
|
||||
${SKIA_CODEC_INCLUDE_DIR}
|
||||
${SKIA_GPU_INCLUDE_DIR}
|
||||
${SKIA_GPU2_INCLUDE_DIR})
|
||||
if(WIN32)
|
||||
include_directories(${SKIA_ANGLE_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
set(SKIA_LIBRARIES
|
||||
${SKIA_LIBRARY}
|
||||
${SKIA_OPENGL_LIBRARY}
|
||||
CACHE INTERNAL "Skia libraries")
|
||||
|
||||
list(APPEND OS_SOURCES
|
||||
skia/skia_display.cpp
|
||||
skia/skia_surface.cpp
|
||||
skia/os.cpp)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND OS_SOURCES
|
||||
skia/skia_window_win.cpp
|
||||
win/keys.cpp
|
||||
win/pen.cpp
|
||||
win/winapi.cpp
|
||||
win/window.cpp
|
||||
win/window_dde.cpp)
|
||||
elseif(APPLE)
|
||||
list(APPEND OS_SOURCES
|
||||
osx/app.mm
|
||||
osx/app_delegate.mm
|
||||
osx/event_queue.mm
|
||||
osx/keys.mm
|
||||
osx/view.mm
|
||||
osx/window.mm
|
||||
skia/skia_window_osx.mm)
|
||||
else()
|
||||
list(APPEND OS_SOURCES
|
||||
skia/skia_window_x11.cpp
|
||||
x11/event_queue.cpp
|
||||
x11/keys.cpp
|
||||
x11/window.cpp
|
||||
x11/x11.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
######################################################################
|
||||
# None backend
|
||||
|
||||
if(USE_NONE_BACKEND)
|
||||
list(APPEND OS_SOURCES
|
||||
none/os.cpp)
|
||||
endif()
|
||||
|
||||
######################################################################
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND OS_SOURCES
|
||||
win/native_dialogs.cpp)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND OS_SOURCES
|
||||
osx/logger.mm
|
||||
osx/menus.mm
|
||||
osx/native_dialogs.mm)
|
||||
endif()
|
||||
|
||||
if(WITH_GTK_FILE_DIALOG_SUPPORT AND UNIX AND NOT APPLE AND NOT BEOS)
|
||||
# Find gtkmm library
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK gtk+-3.0)
|
||||
include_directories(${GTK_INCLUDE_DIRS})
|
||||
link_directories(${GTK_LIBRARY_DIRS})
|
||||
|
||||
add_definitions(-DASEPRITE_WITH_GTK_FILE_DIALOG_SUPPORT)
|
||||
list(APPEND OS_SOURCES
|
||||
gtk/native_dialogs.cpp)
|
||||
endif()
|
||||
|
||||
add_library(os-lib ${OS_SOURCES})
|
||||
|
||||
target_link_libraries(os-lib
|
||||
ft-lib
|
||||
gfx-lib
|
||||
laf-base
|
||||
${FREETYPE_LIBRARIES})
|
||||
|
||||
if(USE_SKIA_BACKEND)
|
||||
target_link_libraries(os-lib
|
||||
${SKIA_LIBRARIES}
|
||||
${X11_LIBRARIES})
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(os-lib fontconfig Xcursor)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_GTK_FILE_DIALOG_SUPPORT)
|
||||
target_link_libraries(os-lib
|
||||
${GTKMM_LIBRARIES})
|
||||
endif()
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2012-2018 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,8 +0,0 @@
|
||||
# LAF OS Library
|
||||
|
||||
`os` is an abstraction layer to access in different way the Operating
|
||||
System graphics interface. We have our own implementation to create
|
||||
windows and handle mouse/keyboard input. The graphics are rendered
|
||||
using the [Skia](https://skia.org/) library.
|
||||
|
||||
* Minimum Windows platform: Windows Vista
|
@ -1,23 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_CAPABILITIES_H_INCLUDED
|
||||
#define OS_CAPABILITIES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
enum class Capabilities {
|
||||
MultipleDisplays = 1,
|
||||
CanResizeDisplay = 2,
|
||||
DisplayScale = 4,
|
||||
CustomNativeMouseCursor = 8,
|
||||
GpuAccelerationSwitch = 16
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,53 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_COMMON_FILE_DIALOG_H
|
||||
#define OS_COMMON_FILE_DIALOG_H
|
||||
#pragma once
|
||||
|
||||
#include "os/native_dialogs.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class CommonFileDialog : public FileDialog {
|
||||
public:
|
||||
CommonFileDialog()
|
||||
: m_type(Type::OpenFile) {
|
||||
}
|
||||
|
||||
void dispose() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void setType(const Type type) override {
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void setTitle(const std::string& title) override {
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
void setDefaultExtension(const std::string& extension) override {
|
||||
m_defExtension = extension;
|
||||
}
|
||||
|
||||
void addFilter(const std::string& extension, const std::string& description) override {
|
||||
if (m_defExtension.empty())
|
||||
m_defExtension = extension;
|
||||
|
||||
m_filters.push_back(std::make_pair(extension, description));
|
||||
}
|
||||
|
||||
protected:
|
||||
Type m_type;
|
||||
std::string m_title;
|
||||
std::string m_defExtension;
|
||||
std::vector<std::pair<std::string, std::string>> m_filters;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,90 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/common/freetype_font.h"
|
||||
|
||||
#include "base/string.h"
|
||||
#include "ft/algorithm.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
FreeTypeFont::FreeTypeFont(ft::Lib& lib,
|
||||
const char* filename,
|
||||
const int height)
|
||||
: m_face(lib.open(filename))
|
||||
{
|
||||
if (m_face.isValid())
|
||||
m_face.setSize(height);
|
||||
}
|
||||
|
||||
FreeTypeFont::~FreeTypeFont()
|
||||
{
|
||||
}
|
||||
|
||||
bool FreeTypeFont::isValid() const
|
||||
{
|
||||
return m_face.isValid();
|
||||
}
|
||||
|
||||
void FreeTypeFont::dispose()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
FontType FreeTypeFont::type()
|
||||
{
|
||||
return FontType::kTrueType;
|
||||
}
|
||||
|
||||
int FreeTypeFont::height() const
|
||||
{
|
||||
return int(m_face.height());
|
||||
}
|
||||
|
||||
int FreeTypeFont::textLength(const std::string& str) const
|
||||
{
|
||||
return ft::calc_text_bounds(m_face, str).w;
|
||||
}
|
||||
|
||||
bool FreeTypeFont::isScalable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeTypeFont::setSize(int size)
|
||||
{
|
||||
m_face.setSize(size);
|
||||
}
|
||||
|
||||
void FreeTypeFont::setAntialias(bool antialias)
|
||||
{
|
||||
m_face.setAntialias(antialias);
|
||||
}
|
||||
|
||||
bool FreeTypeFont::hasCodePoint(int codepoint) const
|
||||
{
|
||||
return m_face.hasCodePoint(codepoint);
|
||||
}
|
||||
|
||||
FreeTypeFont* load_free_type_font(ft::Lib& lib,
|
||||
const char* filename,
|
||||
const int height)
|
||||
{
|
||||
FreeTypeFont* font = new FreeTypeFont(lib, filename, height);
|
||||
if (!font->isValid()) {
|
||||
delete font;
|
||||
font = nullptr;
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,49 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_COMMON_FREETYPE_FONT_H_INCLUDED
|
||||
#define OS_COMMON_FREETYPE_FONT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "ft/hb_face.h"
|
||||
#include "ft/lib.h"
|
||||
#include "os/font.h"
|
||||
|
||||
namespace os {
|
||||
class Font;
|
||||
|
||||
class FreeTypeFont : public Font {
|
||||
public:
|
||||
typedef ft::Face Face;
|
||||
|
||||
FreeTypeFont(ft::Lib& lib,
|
||||
const char* filename,
|
||||
const int height);
|
||||
~FreeTypeFont();
|
||||
|
||||
bool isValid() const;
|
||||
void dispose() override;
|
||||
FontType type() override;
|
||||
int height() const override;
|
||||
int textLength(const std::string& str) const override;
|
||||
bool isScalable() const override;
|
||||
void setSize(int size) override;
|
||||
void setAntialias(bool antialias) override;
|
||||
bool hasCodePoint(int codepoint) const override;
|
||||
|
||||
Face& face() { return m_face; }
|
||||
|
||||
private:
|
||||
mutable Face m_face;
|
||||
};
|
||||
|
||||
FreeTypeFont* load_free_type_font(ft::Lib& lib,
|
||||
const char* filename,
|
||||
const int height);
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,97 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_COMMON_GENERIC_SURFACE_H
|
||||
#define OS_COMMON_GENERIC_SURFACE_H
|
||||
#pragma once
|
||||
|
||||
#include "gfx/clip.h"
|
||||
#include "gfx/color.h"
|
||||
#include "os/surface.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
namespace {
|
||||
|
||||
#define MUL_UN8(a, b, t) \
|
||||
((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
|
||||
|
||||
inline gfx::Color blend(const gfx::Color backdrop, gfx::Color src)
|
||||
{
|
||||
if (gfx::geta(backdrop) == 0)
|
||||
return src;
|
||||
else if (gfx::geta(src) == 0)
|
||||
return backdrop;
|
||||
|
||||
int Br, Bg, Bb, Ba;
|
||||
int Sr, Sg, Sb, Sa;
|
||||
int Rr, Rg, Rb, Ra;
|
||||
|
||||
Br = gfx::getr(backdrop);
|
||||
Bg = gfx::getg(backdrop);
|
||||
Bb = gfx::getb(backdrop);
|
||||
Ba = gfx::geta(backdrop);
|
||||
|
||||
Sr = gfx::getr(src);
|
||||
Sg = gfx::getg(src);
|
||||
Sb = gfx::getb(src);
|
||||
Sa = gfx::geta(src);
|
||||
|
||||
int t;
|
||||
Ra = Ba + Sa - MUL_UN8(Ba, Sa, t);
|
||||
Rr = Br + (Sr-Br) * Sa / Ra;
|
||||
Rg = Bg + (Sg-Bg) * Sa / Ra;
|
||||
Rb = Bb + (Sb-Bb) * Sa / Ra;
|
||||
|
||||
return gfx::rgba(Rr, Rg, Rb, Ra);
|
||||
}
|
||||
|
||||
} // anoynmous namespace
|
||||
|
||||
template<typename Base>
|
||||
class GenericDrawColoredRgbaSurface : public Base {
|
||||
public:
|
||||
|
||||
void drawColoredRgbaSurface(const Surface* src, gfx::Color fg, gfx::Color bg, const gfx::Clip& clipbase) override {
|
||||
gfx::Clip clip(clipbase);
|
||||
if (!clip.clip(this->width(),
|
||||
this->height(),
|
||||
src->width(), src->height()))
|
||||
return;
|
||||
|
||||
SurfaceFormatData format;
|
||||
src->getFormat(&format);
|
||||
|
||||
ASSERT(format.format == kRgbaSurfaceFormat);
|
||||
ASSERT(format.bitsPerPixel == 32);
|
||||
|
||||
for (int v=0; v<clip.size.h; ++v) {
|
||||
const uint32_t* ptr = (const uint32_t*)src->getData(
|
||||
clip.src.x, clip.src.y+v);
|
||||
|
||||
for (int u=0; u<clip.size.w; ++u) {
|
||||
gfx::Color dstColor = this->getPixel(clip.dst.x+u, clip.dst.y+v);
|
||||
if (gfx::geta(bg) > 0)
|
||||
dstColor = blend(dstColor, bg);
|
||||
|
||||
uint32_t src = (((*ptr) & format.alphaMask) >> format.alphaShift);
|
||||
if (src > 0) {
|
||||
src = gfx::rgba(gfx::getr(fg),
|
||||
gfx::getg(fg),
|
||||
gfx::getb(fg), src);
|
||||
dstColor = blend(dstColor, src);
|
||||
}
|
||||
|
||||
this->putPixel(dstColor, clip.dst.x+u, clip.dst.y+v);
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,138 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_SPRITE_SHEET_FONT_H
|
||||
#define OS_SPRITE_SHEET_FONT_H
|
||||
#pragma once
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "base/string.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "os/font.h"
|
||||
#include "os/surface.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace os {
|
||||
|
||||
class SpriteSheetFont : public Font {
|
||||
public:
|
||||
|
||||
SpriteSheetFont() : m_sheet(nullptr) {
|
||||
}
|
||||
|
||||
~SpriteSheetFont() {
|
||||
ASSERT(m_sheet);
|
||||
m_sheet->dispose();
|
||||
}
|
||||
|
||||
void dispose() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
FontType type() override {
|
||||
return FontType::kSpriteSheet;
|
||||
}
|
||||
|
||||
int height() const override {
|
||||
return getCharBounds(' ').h;
|
||||
}
|
||||
|
||||
int textLength(const std::string& str) const override {
|
||||
base::utf8_const_iterator it(str.begin()), end(str.end());
|
||||
int x = 0;
|
||||
while (it != end) {
|
||||
x += getCharBounds(*it).w;
|
||||
++it;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
bool isScalable() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setSize(int size) override {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void setAntialias(bool antialias) override {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
bool hasCodePoint(int codepoint) const override {
|
||||
codepoint -= (int)' ';
|
||||
return (codepoint >= 0 && codepoint < (int)m_chars.size());
|
||||
}
|
||||
|
||||
Surface* getSurfaceSheet() const {
|
||||
return m_sheet;
|
||||
}
|
||||
|
||||
gfx::Rect getCharBounds(int chr) const {
|
||||
chr -= (int)' ';
|
||||
if (chr >= 0 && chr < (int)m_chars.size())
|
||||
return m_chars[chr];
|
||||
else if (chr != 128)
|
||||
return getCharBounds(128);
|
||||
else
|
||||
return gfx::Rect();
|
||||
}
|
||||
|
||||
static Font* fromSurface(Surface* sur) {
|
||||
SpriteSheetFont* font = new SpriteSheetFont;
|
||||
font->m_sheet = sur;
|
||||
|
||||
SurfaceLock lock(sur);
|
||||
gfx::Rect bounds(0, 0, 1, 1);
|
||||
|
||||
while (font->findChar(sur, sur->width(), sur->height(), bounds)) {
|
||||
font->m_chars.push_back(bounds);
|
||||
bounds.x += bounds.w;
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool findChar(const Surface* sur, int width, int height, gfx::Rect& bounds) {
|
||||
gfx::Color keyColor = sur->getPixel(0, 0);
|
||||
|
||||
while (sur->getPixel(bounds.x, bounds.y) == keyColor) {
|
||||
bounds.x++;
|
||||
if (bounds.x >= width) {
|
||||
bounds.x = 0;
|
||||
bounds.y += bounds.h;
|
||||
bounds.h = 1;
|
||||
if (bounds.y >= height)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bounds.w = 0;
|
||||
while ((bounds.x+bounds.w < width) &&
|
||||
(sur->getPixel(bounds.x+bounds.w, bounds.y) != keyColor)) {
|
||||
bounds.w++;
|
||||
}
|
||||
|
||||
bounds.h = 0;
|
||||
while ((bounds.y+bounds.h < height) &&
|
||||
(sur->getPixel(bounds.x, bounds.y+bounds.h) != keyColor)) {
|
||||
bounds.h++;
|
||||
}
|
||||
|
||||
return !bounds.isEmpty();
|
||||
}
|
||||
|
||||
private:
|
||||
Surface* m_sheet;
|
||||
std::vector<gfx::Rect> m_chars;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,139 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_COMMON_SYSTEM_H
|
||||
#define OS_COMMON_SYSTEM_H
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "os/win/native_dialogs.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "os/osx/menus.h"
|
||||
#include "os/osx/native_dialogs.h"
|
||||
#elif defined(ASEPRITE_WITH_GTK_FILE_DIALOG_SUPPORT) && defined(__linux__)
|
||||
#include "os/gtk/native_dialogs.h"
|
||||
#else
|
||||
#include "os/native_dialogs.h"
|
||||
#endif
|
||||
|
||||
#include "ft/lib.h"
|
||||
#include "os/common/freetype_font.h"
|
||||
#include "os/common/sprite_sheet_font.h"
|
||||
#include "os/menus.h"
|
||||
#include "os/system.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace os {
|
||||
|
||||
#ifdef __APPLE__
|
||||
Logger* getOsxLogger();
|
||||
#endif
|
||||
|
||||
class CommonSystem : public System {
|
||||
public:
|
||||
CommonSystem()
|
||||
: m_nativeDialogs(nullptr)
|
||||
, m_menus(nullptr) {
|
||||
#ifdef _WIN32
|
||||
m_useWintabAPI = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
~CommonSystem() {
|
||||
delete m_nativeDialogs;
|
||||
delete m_menus;
|
||||
}
|
||||
|
||||
void dispose() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void useWintabAPI(bool state) override {
|
||||
#ifdef _WIN32
|
||||
m_useWintabAPI = state;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool useWintabAPI() const {
|
||||
return m_useWintabAPI;
|
||||
}
|
||||
#endif
|
||||
|
||||
Logger* logger() override {
|
||||
#ifdef __APPLE__
|
||||
return getOsxLogger();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
Menus* menus() override {
|
||||
#ifdef __APPLE__
|
||||
if (!m_menus)
|
||||
m_menus = new MenusOSX();
|
||||
#endif
|
||||
return m_menus;
|
||||
}
|
||||
|
||||
NativeDialogs* nativeDialogs() override {
|
||||
#ifdef _WIN32
|
||||
if (!m_nativeDialogs)
|
||||
m_nativeDialogs = new NativeDialogsWin32();
|
||||
#elif defined(__APPLE__)
|
||||
if (!m_nativeDialogs)
|
||||
m_nativeDialogs = new NativeDialogsOSX();
|
||||
#elif defined(ASEPRITE_WITH_GTK_FILE_DIALOG_SUPPORT) && defined(__linux__)
|
||||
if (!m_nativeDialogs)
|
||||
m_nativeDialogs = new NativeDialogsGTK();
|
||||
#endif
|
||||
return m_nativeDialogs;
|
||||
}
|
||||
|
||||
Font* loadSpriteSheetFont(const char* filename, int scale) override {
|
||||
Surface* sheet = loadRgbaSurface(filename);
|
||||
Font* font = nullptr;
|
||||
if (sheet) {
|
||||
sheet->applyScale(scale);
|
||||
font = SpriteSheetFont::fromSurface(sheet);
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
Font* loadTrueTypeFont(const char* filename, int height) override {
|
||||
if (!m_ft)
|
||||
m_ft.reset(new ft::Lib());
|
||||
return load_free_type_font(*m_ft.get(), filename, height);
|
||||
}
|
||||
|
||||
KeyModifiers keyModifiers() override {
|
||||
return
|
||||
(KeyModifiers)
|
||||
((isKeyPressed(kKeyLShift) ||
|
||||
isKeyPressed(kKeyRShift) ? kKeyShiftModifier: 0) |
|
||||
(isKeyPressed(kKeyLControl) ||
|
||||
isKeyPressed(kKeyRControl) ? kKeyCtrlModifier: 0) |
|
||||
(isKeyPressed(kKeyAlt) ? kKeyAltModifier: 0) |
|
||||
(isKeyPressed(kKeyAltGr) ? (kKeyCtrlModifier | kKeyAltModifier): 0) |
|
||||
(isKeyPressed(kKeyCommand) ? kKeyCmdModifier: 0) |
|
||||
(isKeyPressed(kKeySpace) ? kKeySpaceModifier: 0) |
|
||||
(isKeyPressed(kKeyLWin) ||
|
||||
isKeyPressed(kKeyRWin) ? kKeyWinModifier: 0));
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
bool m_useWintabAPI;
|
||||
#endif
|
||||
NativeDialogs* m_nativeDialogs;
|
||||
Menus* m_menus;
|
||||
std::unique_ptr<ft::Lib> m_ft;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,86 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_DISPLAY_H_INCLUDED
|
||||
#define OS_DISPLAY_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "os/display_handle.h"
|
||||
#include "os/native_cursor.h"
|
||||
#include "os/surface_list.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace os {
|
||||
|
||||
class Surface;
|
||||
|
||||
// A display or window to show graphics.
|
||||
class Display {
|
||||
public:
|
||||
virtual ~Display() { }
|
||||
virtual void dispose() = 0;
|
||||
|
||||
// Returns the real and current display's size (without scale applied).
|
||||
virtual int width() const = 0;
|
||||
virtual int height() const = 0;
|
||||
|
||||
// Returns the display when it was not maximized.
|
||||
virtual int originalWidth() const = 0;
|
||||
virtual int originalHeight() const = 0;
|
||||
|
||||
// Returns the current display scale. Each pixel in the internal
|
||||
// display surface, is represented by SCALExSCALE pixels on the
|
||||
// screen.
|
||||
virtual int scale() const = 0;
|
||||
|
||||
// Changes the scale.
|
||||
// The available surface size will be (Display::width() / scale,
|
||||
// Display::height() / scale)
|
||||
virtual void setScale(int scale) = 0;
|
||||
|
||||
// Returns the main surface to draw into this display.
|
||||
// You must not dispose this surface.
|
||||
virtual Surface* getSurface() = 0;
|
||||
|
||||
// Flips all graphics in the surface to the real display.
|
||||
virtual void flip(const gfx::Rect& bounds) = 0;
|
||||
|
||||
virtual void maximize() = 0;
|
||||
virtual bool isMaximized() const = 0;
|
||||
virtual bool isMinimized() const = 0;
|
||||
|
||||
virtual void setTitleBar(const std::string& title) = 0;
|
||||
virtual void setIcons(const SurfaceList& icons) = 0;
|
||||
|
||||
virtual NativeCursor nativeMouseCursor() = 0;
|
||||
virtual bool setNativeMouseCursor(NativeCursor cursor) = 0;
|
||||
virtual bool setNativeMouseCursor(const os::Surface* cursor,
|
||||
const gfx::Point& focus,
|
||||
const int scale) = 0;
|
||||
virtual void setMousePosition(const gfx::Point& position) = 0;
|
||||
virtual void captureMouse() = 0;
|
||||
virtual void releaseMouse() = 0;
|
||||
|
||||
// Set/get the specific information to restore the exact same
|
||||
// window position (e.g. in the same monitor).
|
||||
virtual std::string getLayout() = 0;
|
||||
virtual void setLayout(const std::string& layout) = 0;
|
||||
|
||||
// For Windows 8/10 only in tablet devices: Set to true if you
|
||||
// want to interpret one finger as the mouse movement and two
|
||||
// fingers as pan/scroll (true by default). If you want to pan
|
||||
// with one finger, call this function with false.
|
||||
virtual void setInterpretOneFingerGestureAsMouseMovement(bool state) = 0;
|
||||
|
||||
// Returns the HWND on Windows.
|
||||
virtual DisplayHandle nativeHandle() = 0;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_DISPLAY_HANDLE_H_INCLUDED
|
||||
#define OS_DISPLAY_HANDLE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
typedef void* DisplayHandle;
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,228 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/draw_text.h"
|
||||
|
||||
#include "ft/algorithm.h"
|
||||
#include "ft/hb_shaper.h"
|
||||
#include "gfx/clip.h"
|
||||
#include "os/common/freetype_font.h"
|
||||
#include "os/common/generic_surface.h"
|
||||
#include "os/common/sprite_sheet_font.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
gfx::Rect draw_text(Surface* surface, Font* font,
|
||||
const base::utf8_const_iterator& begin,
|
||||
const base::utf8_const_iterator& end,
|
||||
gfx::Color fg, gfx::Color bg,
|
||||
int x, int y,
|
||||
DrawTextDelegate* delegate)
|
||||
{
|
||||
base::utf8_const_iterator it = begin;
|
||||
gfx::Rect textBounds;
|
||||
|
||||
retry:;
|
||||
// Check if this font is enough to draw the given string or we will
|
||||
// need the fallback for some special Unicode chars
|
||||
if (font->fallback()) {
|
||||
// TODO compose unicode characters and check those codepoints, the
|
||||
// same in the drawing code of sprite sheet font
|
||||
for (auto it=begin; it!=end; ++it) {
|
||||
uint32_t code = *it;
|
||||
if (code && !font->hasCodePoint(code)) {
|
||||
Font* newFont = font->fallback();
|
||||
|
||||
// Search a valid fallback
|
||||
while (newFont && !newFont->hasCodePoint(code))
|
||||
newFont = newFont->fallback();
|
||||
if (!newFont)
|
||||
break;
|
||||
|
||||
y += font->height()/2 - newFont->height()/2;
|
||||
|
||||
font = newFont;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (font->type()) {
|
||||
|
||||
case FontType::kSpriteSheet: {
|
||||
SpriteSheetFont* ssFont = static_cast<SpriteSheetFont*>(font);
|
||||
Surface* sheet = ssFont->getSurfaceSheet();
|
||||
|
||||
if (surface) {
|
||||
sheet->lock();
|
||||
surface->lock();
|
||||
}
|
||||
|
||||
while (it != end) {
|
||||
int chr = *it;
|
||||
if (delegate) {
|
||||
int i = it-begin;
|
||||
delegate->preProcessChar(i, chr, fg, bg);
|
||||
}
|
||||
|
||||
gfx::Rect charBounds = ssFont->getCharBounds(chr);
|
||||
gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h);
|
||||
if (delegate && !delegate->preDrawChar(outCharBounds))
|
||||
break;
|
||||
|
||||
if (!charBounds.isEmpty()) {
|
||||
if (surface)
|
||||
surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds));
|
||||
}
|
||||
|
||||
textBounds |= outCharBounds;
|
||||
if (delegate)
|
||||
delegate->postDrawChar(outCharBounds);
|
||||
|
||||
x += charBounds.w;
|
||||
++it;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
surface->unlock();
|
||||
sheet->unlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FontType::kTrueType: {
|
||||
FreeTypeFont* ttFont = static_cast<FreeTypeFont*>(font);
|
||||
bool antialias = ttFont->face().antialias();
|
||||
int fg_alpha = gfx::geta(fg);
|
||||
|
||||
gfx::Rect clipBounds;
|
||||
os::SurfaceFormatData fd;
|
||||
if (surface) {
|
||||
clipBounds = surface->getClipBounds();
|
||||
surface->getFormat(&fd);
|
||||
surface->lock();
|
||||
}
|
||||
|
||||
ft::ForEachGlyph<FreeTypeFont::Face> feg(ttFont->face());
|
||||
if (feg.initialize(it, end)) {
|
||||
do {
|
||||
if (delegate) {
|
||||
delegate->preProcessChar(feg.charIndex(),
|
||||
feg.unicodeChar(), fg, bg);
|
||||
}
|
||||
|
||||
auto glyph = feg.glyph();
|
||||
if (!glyph)
|
||||
continue;
|
||||
|
||||
gfx::Rect origDstBounds(
|
||||
x + int(glyph->startX),
|
||||
y + int(glyph->y),
|
||||
int(glyph->endX) - int(glyph->startX),
|
||||
int(glyph->bitmap->rows) ? int(glyph->bitmap->rows): 1);
|
||||
|
||||
if (delegate && !delegate->preDrawChar(origDstBounds))
|
||||
break;
|
||||
|
||||
origDstBounds.x = x + int(glyph->x);
|
||||
origDstBounds.w = int(glyph->bitmap->width);
|
||||
origDstBounds.h = int(glyph->bitmap->rows);
|
||||
|
||||
gfx::Rect dstBounds = origDstBounds;
|
||||
if (surface)
|
||||
dstBounds &= clipBounds;
|
||||
|
||||
if (surface && !dstBounds.isEmpty()) {
|
||||
int clippedRows = dstBounds.y - origDstBounds.y;
|
||||
int dst_y = dstBounds.y;
|
||||
int t;
|
||||
for (int v=0; v<dstBounds.h; ++v, ++dst_y) {
|
||||
int bit = 0;
|
||||
const uint8_t* p = glyph->bitmap->buffer
|
||||
+ (v+clippedRows)*glyph->bitmap->pitch;
|
||||
int dst_x = dstBounds.x;
|
||||
uint32_t* dst_address =
|
||||
(uint32_t*)surface->getData(dst_x, dst_y);
|
||||
|
||||
// Skip first clipped pixels
|
||||
for (int u=0; u<dstBounds.x-origDstBounds.x; ++u) {
|
||||
if (antialias) {
|
||||
++p;
|
||||
}
|
||||
else {
|
||||
if (bit == 8) {
|
||||
bit = 0;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int u=0; u<dstBounds.w; ++u, ++dst_x) {
|
||||
ASSERT(clipBounds.contains(gfx::Point(dst_x, dst_y)));
|
||||
|
||||
int alpha;
|
||||
if (antialias) {
|
||||
alpha = *(p++);
|
||||
}
|
||||
else {
|
||||
alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0);
|
||||
if (bit == 8) {
|
||||
bit = 0;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t backdrop = *dst_address;
|
||||
gfx::Color backdropColor =
|
||||
gfx::rgba(
|
||||
((backdrop & fd.redMask) >> fd.redShift),
|
||||
((backdrop & fd.greenMask) >> fd.greenShift),
|
||||
((backdrop & fd.blueMask) >> fd.blueShift),
|
||||
((backdrop & fd.alphaMask) >> fd.alphaShift));
|
||||
|
||||
gfx::Color output = gfx::rgba(gfx::getr(fg),
|
||||
gfx::getg(fg),
|
||||
gfx::getb(fg),
|
||||
MUL_UN8(fg_alpha, alpha, t));
|
||||
if (gfx::geta(bg) > 0)
|
||||
output = blend(blend(backdropColor, bg), output);
|
||||
else
|
||||
output = blend(backdropColor, output);
|
||||
|
||||
*dst_address =
|
||||
((gfx::getr(output) << fd.redShift ) & fd.redMask ) |
|
||||
((gfx::getg(output) << fd.greenShift) & fd.greenMask) |
|
||||
((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) |
|
||||
((gfx::geta(output) << fd.alphaShift) & fd.alphaMask);
|
||||
|
||||
++dst_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!origDstBounds.w) origDstBounds.w = 1;
|
||||
if (!origDstBounds.h) origDstBounds.h = 1;
|
||||
textBounds |= origDstBounds;
|
||||
if (delegate)
|
||||
delegate->postDrawChar(origDstBounds);
|
||||
} while (feg.nextChar());
|
||||
}
|
||||
|
||||
if (surface)
|
||||
surface->unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return textBounds;
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,54 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_DRAW_TEXT_H_INCLUDED
|
||||
#define OS_DRAW_TEXT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/string.h"
|
||||
#include "gfx/color.h"
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class Font;
|
||||
class Surface;
|
||||
class SurfaceLock;
|
||||
|
||||
class DrawTextDelegate {
|
||||
public:
|
||||
virtual ~DrawTextDelegate() { }
|
||||
|
||||
// This is called before drawing the character.
|
||||
virtual void preProcessChar(const int index,
|
||||
const int codepoint,
|
||||
gfx::Color& fg, gfx::Color& bg) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
virtual bool preDrawChar(const gfx::Rect& charBounds) {
|
||||
// Returns false if the process should stop here.
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void postDrawChar(const gfx::Rect& charBounds) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
// The surface can be nullptr just to process the string
|
||||
// (e.g. measure how much space will use the text without drawing
|
||||
// it).
|
||||
gfx::Rect draw_text(Surface* surface, Font* font,
|
||||
const base::utf8_const_iterator& begin,
|
||||
const base::utf8_const_iterator& end,
|
||||
gfx::Color fg, gfx::Color bg,
|
||||
int x, int y,
|
||||
DrawTextDelegate* delegate);
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_ERROR_H_INCLUDED
|
||||
#define OS_ERROR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
void error_message(const char* msg);
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
135
src/os/event.h
135
src/os/event.h
@ -1,135 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_EVENT_H_INCLUDED
|
||||
#define OS_EVENT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/paths.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/size.h"
|
||||
#include "os/keys.h"
|
||||
#include "os/pointer_type.h"
|
||||
|
||||
#pragma push_macro("None")
|
||||
#undef None // Undefine the X11 None macro
|
||||
|
||||
namespace os {
|
||||
|
||||
class Display;
|
||||
|
||||
class Event {
|
||||
public:
|
||||
enum Type {
|
||||
None,
|
||||
CloseDisplay,
|
||||
ResizeDisplay,
|
||||
DropFiles,
|
||||
MouseEnter,
|
||||
MouseLeave,
|
||||
MouseMove,
|
||||
MouseDown,
|
||||
MouseUp,
|
||||
MouseWheel,
|
||||
MouseDoubleClick,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
TouchMagnify,
|
||||
};
|
||||
|
||||
enum MouseButton {
|
||||
NoneButton,
|
||||
LeftButton,
|
||||
RightButton,
|
||||
MiddleButton,
|
||||
X1Button,
|
||||
X2Button,
|
||||
};
|
||||
|
||||
Event() : m_type(None),
|
||||
m_display(nullptr),
|
||||
m_scancode(kKeyNil),
|
||||
m_modifiers(kKeyUninitializedModifier),
|
||||
m_unicodeChar(0),
|
||||
m_isDead(false),
|
||||
m_repeat(0),
|
||||
m_preciseWheel(false),
|
||||
m_pointerType(PointerType::Unknown),
|
||||
m_button(NoneButton),
|
||||
m_magnification(0.0),
|
||||
m_pressure(0.0) {
|
||||
}
|
||||
|
||||
Type type() const { return m_type; }
|
||||
Display* display() const { return m_display; }
|
||||
const base::paths& files() const { return m_files; }
|
||||
// TODO Rename this to virtualKey(), which is the real
|
||||
// meaning. Then we need another kind of "scan code" with the
|
||||
// position in the keyboard, which might be useful to identify
|
||||
// keys by its position (e.g. WASD keys in other keyboard
|
||||
// layouts).
|
||||
KeyScancode scancode() const { return m_scancode; }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
int unicodeChar() const { return m_unicodeChar; }
|
||||
bool isDeadKey() const { return m_isDead; }
|
||||
int repeat() const { return m_repeat; }
|
||||
gfx::Point position() const { return m_position; }
|
||||
gfx::Point wheelDelta() const { return m_wheelDelta; }
|
||||
|
||||
// We suppose that if we are receiving precise scrolling deltas,
|
||||
// it means that the user is using a touch-like surface (trackpad,
|
||||
// magic mouse scrolling, touch wacom tablet, etc.)
|
||||
bool preciseWheel() const { return m_preciseWheel; }
|
||||
|
||||
PointerType pointerType() const { return m_pointerType; }
|
||||
MouseButton button() const { return m_button; }
|
||||
double magnification() const { return m_magnification; }
|
||||
double pressure() const { return m_pressure; }
|
||||
|
||||
void setType(Type type) { m_type = type; }
|
||||
void setDisplay(Display* display) { m_display = display; }
|
||||
void setFiles(const base::paths& files) { m_files = files; }
|
||||
|
||||
void setScancode(KeyScancode scancode) { m_scancode = scancode; }
|
||||
void setModifiers(KeyModifiers modifiers) { m_modifiers = modifiers; }
|
||||
void setUnicodeChar(int unicodeChar) { m_unicodeChar = unicodeChar; }
|
||||
void setDeadKey(bool state) { m_isDead = state; }
|
||||
void setRepeat(int repeat) { m_repeat = repeat; }
|
||||
void setPosition(const gfx::Point& pos) { m_position = pos; }
|
||||
void setWheelDelta(const gfx::Point& delta) { m_wheelDelta = delta; }
|
||||
void setPreciseWheel(bool precise) { m_preciseWheel = precise; }
|
||||
void setPointerType(PointerType pointerType) { m_pointerType = pointerType; }
|
||||
void setButton(MouseButton button) { m_button = button; }
|
||||
void setMagnification(double magnification) { m_magnification = magnification; }
|
||||
void setPressure(double pressure) { m_pressure = pressure; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
Display* m_display;
|
||||
base::paths m_files;
|
||||
KeyScancode m_scancode;
|
||||
KeyModifiers m_modifiers;
|
||||
int m_unicodeChar;
|
||||
bool m_isDead;
|
||||
int m_repeat; // repeat=0 means the first time the key is pressed
|
||||
gfx::Point m_position;
|
||||
gfx::Point m_wheelDelta;
|
||||
bool m_preciseWheel;
|
||||
PointerType m_pointerType;
|
||||
MouseButton m_button;
|
||||
|
||||
// For TouchMagnify event
|
||||
double m_magnification;
|
||||
|
||||
// Pressure of stylus used in mouse-like events
|
||||
double m_pressure;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#pragma pop_macro("None")
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_EVENT_QUEUE_H_INCLUDED
|
||||
#define OS_EVENT_QUEUE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
class Event;
|
||||
|
||||
class EventQueue {
|
||||
public:
|
||||
virtual ~EventQueue() { }
|
||||
virtual void getEvent(Event& ev, bool canWait) = 0;
|
||||
virtual void queueEvent(const Event& ev) = 0;
|
||||
|
||||
// On MacOS X we need the EventQueue before the creation of the
|
||||
// System. E.g. when we double-click a file an Event to open that
|
||||
// file is queued in application:openFile:, code which is executed
|
||||
// before the user's main() code.
|
||||
static EventQueue* instance();
|
||||
};
|
||||
|
||||
inline void queue_event(const Event& ev) {
|
||||
EventQueue::instance()->queueEvent(ev);
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_FONT_H_INCLUDED
|
||||
#define OS_FONT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace os {
|
||||
|
||||
enum class FontType {
|
||||
kUnknown,
|
||||
kSpriteSheet,
|
||||
kTrueType,
|
||||
};
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Font() : m_fallback(nullptr) { }
|
||||
virtual ~Font() { }
|
||||
virtual void dispose() = 0;
|
||||
virtual FontType type() = 0;
|
||||
virtual int height() const = 0;
|
||||
virtual int textLength(const std::string& str) const = 0;
|
||||
virtual bool isScalable() const = 0;
|
||||
virtual void setSize(int size) = 0;
|
||||
virtual void setAntialias(bool antialias) = 0;
|
||||
virtual bool hasCodePoint(int codepoint) const = 0;
|
||||
|
||||
os::Font* fallback() const {
|
||||
return m_fallback;
|
||||
}
|
||||
void setFallback(os::Font* font) {
|
||||
m_fallback = font;
|
||||
}
|
||||
|
||||
private:
|
||||
os::Font* m_fallback;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,25 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_GL_CONTEXT_INCLUDED
|
||||
#define OS_GL_CONTEXT_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
class GLContext {
|
||||
public:
|
||||
virtual ~GLContext() { }
|
||||
virtual bool createGLContext() = 0;
|
||||
virtual void destroyGLContext() = 0;
|
||||
virtual int getStencilBits() = 0;
|
||||
virtual int getSampleCount() = 0;
|
||||
virtual void swapBuffers() { }
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_GL_CONTEXT_CGL_INCLUDED
|
||||
#define OS_GL_CONTEXT_CGL_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/gl/gl_context.h"
|
||||
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace os {
|
||||
|
||||
class GLContextCGL : public GLContext {
|
||||
public:
|
||||
GLContextCGL()
|
||||
: m_glctx(nullptr)
|
||||
, m_stencilBits(0)
|
||||
, m_sampleCount(0) {
|
||||
}
|
||||
|
||||
~GLContextCGL() {
|
||||
destroyGLContext();
|
||||
}
|
||||
|
||||
bool createGLContext() override {
|
||||
CGLPixelFormatAttribute attributes[] = {
|
||||
kCGLPFAOpenGLProfile,
|
||||
(CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core,
|
||||
kCGLPFAAccelerated,
|
||||
kCGLPFADoubleBuffer,
|
||||
(CGLPixelFormatAttribute)0
|
||||
};
|
||||
CGLPixelFormatObj format;
|
||||
GLint npix;
|
||||
CGLChoosePixelFormat(attributes, &format, &npix);
|
||||
if (!format)
|
||||
return false;
|
||||
|
||||
CGLDescribePixelFormat(format, 0, kCGLPFASamples, &m_sampleCount);
|
||||
CGLDescribePixelFormat(format, 0, kCGLPFAStencilSize, &m_stencilBits);
|
||||
|
||||
CGLCreateContext(format, nullptr, &m_glctx);
|
||||
CGLReleasePixelFormat(format);
|
||||
if (!m_glctx)
|
||||
return false;
|
||||
|
||||
CGLSetCurrentContext(m_glctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroyGLContext() override {
|
||||
if (m_glctx) {
|
||||
CGLReleaseContext(m_glctx);
|
||||
m_glctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int getStencilBits() override {
|
||||
return m_stencilBits;
|
||||
}
|
||||
|
||||
int getSampleCount() override {
|
||||
return m_sampleCount;
|
||||
}
|
||||
|
||||
CGLContextObj cglContext() {
|
||||
return m_glctx;
|
||||
}
|
||||
|
||||
private:
|
||||
CGLContextObj m_glctx;
|
||||
int m_stencilBits;
|
||||
int m_sampleCount;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,165 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_GL_CONTEXT_EGL_INCLUDED
|
||||
#define OS_GL_CONTEXT_EGL_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/gl/gl_context.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
namespace os {
|
||||
|
||||
class GLContextEGL : public GLContext {
|
||||
public:
|
||||
GLContextEGL(void* nativeDisplay)
|
||||
: m_nativeDisplay(nativeDisplay)
|
||||
, m_context(EGL_NO_CONTEXT)
|
||||
, m_display(EGL_NO_DISPLAY)
|
||||
, m_surface(EGL_NO_SURFACE)
|
||||
{
|
||||
}
|
||||
|
||||
~GLContextEGL() {
|
||||
destroyGLContext();
|
||||
}
|
||||
|
||||
bool createGLContext() override {
|
||||
m_display = getD3DEGLDisplay((HDC)GetDC((HWND)m_nativeDisplay));
|
||||
if (m_display == EGL_NO_DISPLAY) {
|
||||
LOG("OS: Cannot create EGL display");
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLint majorVersion;
|
||||
EGLint minorVersion;
|
||||
if (!eglInitialize(m_display, &majorVersion, &minorVersion))
|
||||
return false;
|
||||
|
||||
static const EGLint configAttribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, 8,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLConfig surfaceConfig;
|
||||
EGLint numConfigs;
|
||||
if (!eglChooseConfig(m_display, configAttribs, &surfaceConfig, 1, &numConfigs))
|
||||
return false;
|
||||
|
||||
static const EGLint surfaceAttribs[] = {
|
||||
EGL_WIDTH, 1,
|
||||
EGL_HEIGHT, 1,
|
||||
EGL_NONE
|
||||
};
|
||||
m_surface = eglCreateWindowSurface(
|
||||
m_display,
|
||||
surfaceConfig,
|
||||
(EGLNativeWindowType)m_nativeDisplay,
|
||||
surfaceAttribs);
|
||||
if (m_surface == EGL_NO_SURFACE)
|
||||
return false;
|
||||
|
||||
static const EGLint contextAttribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
m_context = eglCreateContext(m_display, surfaceConfig,
|
||||
EGL_NO_CONTEXT,
|
||||
contextAttribs);
|
||||
if (m_context == EGL_NO_CONTEXT)
|
||||
return false;
|
||||
|
||||
if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
||||
return false;
|
||||
|
||||
eglGetConfigAttrib(m_display, surfaceConfig, EGL_STENCIL_SIZE, &m_stencilBits);
|
||||
eglGetConfigAttrib(m_display, surfaceConfig, EGL_SAMPLES, &m_sampleCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroyGLContext() override {
|
||||
if (m_display != EGL_NO_DISPLAY) {
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
if (m_surface != EGL_NO_SURFACE) {
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
m_surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
if (m_context != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(m_display, m_context);
|
||||
m_context = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
eglTerminate(m_display);
|
||||
m_display = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
|
||||
int getStencilBits() override {
|
||||
return m_stencilBits;
|
||||
}
|
||||
|
||||
int getSampleCount() override {
|
||||
return m_sampleCount;
|
||||
}
|
||||
|
||||
void swapBuffers() override {
|
||||
eglSwapBuffers(m_display, m_surface);
|
||||
}
|
||||
|
||||
private:
|
||||
static void* getD3DEGLDisplay(void* nativeDisplay) {
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
|
||||
eglGetPlatformDisplayEXT =
|
||||
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
if (!eglGetPlatformDisplayEXT)
|
||||
return eglGetDisplay(static_cast<EGLNativeDisplayType>(nativeDisplay));
|
||||
|
||||
EGLint attribs[3][3] = {
|
||||
{
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
EGL_NONE
|
||||
},
|
||||
{
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
|
||||
EGL_NONE
|
||||
},
|
||||
{
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE,
|
||||
EGL_NONE
|
||||
}
|
||||
};
|
||||
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
for (int i=0; i<3 && display == EGL_NO_DISPLAY; ++i) {
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
|
||||
nativeDisplay, attribs[i]);
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
void* m_nativeDisplay;
|
||||
void* m_context;
|
||||
void* m_display;
|
||||
void* m_surface;
|
||||
EGLint m_stencilBits;
|
||||
EGLint m_sampleCount;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,92 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_GL_CONTEXT_WGL_INCLUDED
|
||||
#define OS_GL_CONTEXT_WGL_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/gl/gl_context.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace os {
|
||||
|
||||
class GLContextWGL : public GLContext {
|
||||
public:
|
||||
GLContextWGL(HWND hwnd)
|
||||
: m_hwnd(hwnd)
|
||||
, m_glrc(nullptr) {
|
||||
}
|
||||
|
||||
~GLContextWGL() {
|
||||
destroyGLContext();
|
||||
}
|
||||
|
||||
bool createGLContext() override {
|
||||
HDC hdc = GetDC(m_hwnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd = {
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1, // version number
|
||||
PFD_DRAW_TO_WINDOW | // support window
|
||||
PFD_SUPPORT_OPENGL, // support OpenGL
|
||||
PFD_TYPE_RGBA, // RGBA type
|
||||
24, // 24-bit color depth
|
||||
0, 0, 0, 0, 0, 0, // color bits ignored
|
||||
8, // 8-bit alpha buffer
|
||||
0, // shift bit ignored
|
||||
0, // no accumulation buffer
|
||||
0, 0, 0, 0, // accum bits ignored
|
||||
0, // no z-buffer
|
||||
0, // no stencil buffer
|
||||
0, // no auxiliary buffer
|
||||
PFD_MAIN_PLANE, // main layer
|
||||
0, // reserved
|
||||
0, 0, 0 // layer masks ignored
|
||||
};
|
||||
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
|
||||
SetPixelFormat(hdc, pixelFormat, &pfd);
|
||||
|
||||
m_glrc = wglCreateContext(hdc);
|
||||
if (!m_glrc) {
|
||||
ReleaseDC(m_hwnd, hdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
wglMakeCurrent(hdc, m_glrc);
|
||||
ReleaseDC(m_hwnd, hdc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroyGLContext() override {
|
||||
if (m_glrc) {
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(m_glrc);
|
||||
m_glrc = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int getStencilBits() override {
|
||||
HDC hdc = GetDC(m_hwnd);
|
||||
int pixelFormat = GetPixelFormat(hdc);
|
||||
PIXELFORMATDESCRIPTOR pfd;
|
||||
DescribePixelFormat(hdc, pixelFormat, sizeof(pfd), &pfd);
|
||||
ReleaseDC(m_hwnd, hdc);
|
||||
return pfd.cStencilBits;
|
||||
}
|
||||
|
||||
int getSampleCount() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
HWND m_hwnd;
|
||||
HGLRC m_glrc;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,258 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017-2018 David Capello
|
||||
// Copyright (C) 2016 Gabriel Rauter
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/gtk/native_dialogs.h"
|
||||
|
||||
#include "base/fs.h"
|
||||
#include "base/string.h"
|
||||
#include "os/common/file_dialog.h"
|
||||
#include "os/display.h"
|
||||
#include "os/error.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
namespace os {
|
||||
|
||||
class FileDialogGTK : public CommonFileDialog {
|
||||
public:
|
||||
FileDialogGTK() {
|
||||
}
|
||||
|
||||
std::string fileName() override {
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
void getMultipleFileNames(base::paths& output) override {
|
||||
output = m_filenames;
|
||||
}
|
||||
|
||||
void setFileName(const std::string& filename) override {
|
||||
m_filename = base::get_file_name(filename);
|
||||
m_initialDir = base::get_file_path(filename);
|
||||
}
|
||||
|
||||
bool show(Display* parent) override {
|
||||
static std::string s_lastUsedDir;
|
||||
if (s_lastUsedDir.empty())
|
||||
s_lastUsedDir = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
|
||||
|
||||
const char* okLabel;
|
||||
GtkFileChooserAction action;
|
||||
|
||||
switch (m_type) {
|
||||
case Type::OpenFile:
|
||||
case Type::OpenFiles:
|
||||
action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
okLabel = "_Open";
|
||||
break;
|
||||
case Type::OpenFolder:
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
okLabel = "_Open Folder";
|
||||
break;
|
||||
case Type::SaveFile:
|
||||
action = GTK_FILE_CHOOSER_ACTION_SAVE;
|
||||
okLabel = "_Save";
|
||||
break;
|
||||
}
|
||||
|
||||
// GtkWindow* gtkParent = nullptr;
|
||||
GtkWidget* dialog = gtk_file_chooser_dialog_new(
|
||||
m_title.c_str(),
|
||||
nullptr,
|
||||
action,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
okLabel, GTK_RESPONSE_ACCEPT,
|
||||
nullptr);
|
||||
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
|
||||
|
||||
GtkFileChooser* chooser = GTK_FILE_CHOOSER(dialog);
|
||||
m_chooser = chooser;
|
||||
|
||||
if (m_type == Type::SaveFile)
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
|
||||
else if (m_type == Type::OpenFiles)
|
||||
gtk_file_chooser_set_select_multiple(chooser, true);
|
||||
|
||||
if (m_type != Type::OpenFolder) {
|
||||
setupFilters(base::get_file_extension(m_filename));
|
||||
setupPreview();
|
||||
}
|
||||
|
||||
if (m_initialDir.empty())
|
||||
gtk_file_chooser_set_current_folder(chooser, s_lastUsedDir.c_str());
|
||||
else
|
||||
gtk_file_chooser_set_current_folder(chooser, m_initialDir.c_str());
|
||||
|
||||
if (!m_filename.empty()) {
|
||||
std::string fn = m_filename;
|
||||
// Add default extension
|
||||
if (m_type == Type::SaveFile && base::get_file_extension(fn).empty()) {
|
||||
fn.push_back('.');
|
||||
fn += m_defExtension;
|
||||
}
|
||||
gtk_file_chooser_set_current_name(chooser, fn.c_str());
|
||||
}
|
||||
|
||||
// Setup the "parent" display as the parent of the dialog (we've
|
||||
// to convert a X11 Window into a GdkWindow to do this).
|
||||
GdkWindow* gdkParentWindow = nullptr;
|
||||
if (parent) {
|
||||
GdkWindow* gdkWindow = gtk_widget_get_root_window(dialog);
|
||||
|
||||
gdkParentWindow =
|
||||
gdk_x11_window_foreign_new_for_display(
|
||||
gdk_window_get_display(gdkWindow),
|
||||
(::Window)parent->nativeHandle());
|
||||
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
|
||||
gdk_window_set_transient_for(gdkWindow, gdkParentWindow);
|
||||
}
|
||||
else {
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
|
||||
}
|
||||
|
||||
// Show the dialog
|
||||
gint res = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
if (res == GTK_RESPONSE_ACCEPT) {
|
||||
s_lastUsedDir = gtk_file_chooser_get_current_folder(chooser);
|
||||
m_filename = gtk_file_chooser_get_filename(chooser);
|
||||
|
||||
if (m_type == Type::OpenFiles) {
|
||||
GSList* list = gtk_file_chooser_get_filenames(chooser);
|
||||
g_slist_foreach(
|
||||
list,
|
||||
[](void* fn, void* userdata){
|
||||
auto self = (FileDialogGTK*)userdata;
|
||||
self->m_filenames.push_back((char*)fn);
|
||||
g_free(fn);
|
||||
}, this);
|
||||
g_slist_free(list);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
if (gdkParentWindow)
|
||||
g_object_unref(gdkParentWindow);
|
||||
|
||||
// Pump gtk+ events to finally hide the dialog from the screen
|
||||
while (gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
|
||||
return (res == GTK_RESPONSE_ACCEPT);
|
||||
}
|
||||
|
||||
private:
|
||||
void setupFilters(const std::string& fnExtension) {
|
||||
// Filter for all known formats
|
||||
GtkFileFilter* gtkFilter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(gtkFilter, "All formats");
|
||||
for (const auto& filter : m_filters) {
|
||||
const std::string& ext = filter.first;
|
||||
std::string pat = "*." + ext;
|
||||
gtk_file_filter_add_pattern(gtkFilter, pat.c_str());
|
||||
}
|
||||
gtk_file_chooser_add_filter(m_chooser, gtkFilter);
|
||||
|
||||
// One filter for each format
|
||||
for (const auto& filter : m_filters) {
|
||||
const std::string& ext = filter.first;
|
||||
const std::string& desc = filter.second;
|
||||
std::string pat = "*." + ext;
|
||||
|
||||
gtkFilter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(gtkFilter, desc.c_str());
|
||||
gtk_file_filter_add_pattern(gtkFilter, pat.c_str());
|
||||
gtk_file_chooser_add_filter(m_chooser, gtkFilter);
|
||||
|
||||
if (base::utf8_icmp(ext, fnExtension) == 0)
|
||||
gtk_file_chooser_set_filter(m_chooser, gtkFilter);
|
||||
}
|
||||
|
||||
// One filter for all files
|
||||
gtkFilter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(gtkFilter, "All files");
|
||||
gtk_file_filter_add_pattern(gtkFilter, "*");
|
||||
gtk_file_chooser_add_filter(m_chooser, gtkFilter);
|
||||
}
|
||||
|
||||
void setupPreview() {
|
||||
m_preview = gtk_image_new();
|
||||
|
||||
gtk_file_chooser_set_use_preview_label(m_chooser, false);
|
||||
gtk_file_chooser_set_preview_widget(m_chooser, m_preview);
|
||||
|
||||
g_signal_connect(
|
||||
m_chooser, "update-preview",
|
||||
G_CALLBACK(&FileDialogGTK::s_onUpdatePreview), this);
|
||||
}
|
||||
|
||||
static void s_onUpdatePreview(GtkFileChooser* chooser, gpointer userData) {
|
||||
((FileDialogGTK*)userData)->onUpdatePreview();
|
||||
}
|
||||
void onUpdatePreview() {
|
||||
// Disable preview because we don't know if we will be able to
|
||||
// load/generate the preview successfully.
|
||||
gtk_file_chooser_set_preview_widget_active(m_chooser, false);
|
||||
|
||||
const char* fn = gtk_file_chooser_get_filename(m_chooser);
|
||||
if (fn && base::is_file(fn)) {
|
||||
GError* err = nullptr;
|
||||
GdkPixbuf* previewPixbuf =
|
||||
gdk_pixbuf_new_from_file_at_scale(fn, 256, 256, true, &err);
|
||||
if (previewPixbuf) {
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(m_preview), previewPixbuf);
|
||||
g_object_unref(previewPixbuf);
|
||||
|
||||
// Now we can enable the preview panel as the preview was
|
||||
// generated.
|
||||
gtk_file_chooser_set_preview_widget_active(m_chooser, true);
|
||||
}
|
||||
if (err)
|
||||
g_error_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
std::string m_filename;
|
||||
std::string m_initialDir;
|
||||
base::paths m_filenames;
|
||||
GtkFileChooser* m_chooser;
|
||||
GtkWidget* m_preview;
|
||||
};
|
||||
|
||||
NativeDialogsGTK::NativeDialogsGTK()
|
||||
: m_gtkApp(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
NativeDialogsGTK::~NativeDialogsGTK()
|
||||
{
|
||||
if (m_gtkApp) {
|
||||
g_object_unref(m_gtkApp);
|
||||
m_gtkApp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog* NativeDialogsGTK::createFileDialog()
|
||||
{
|
||||
if (!m_gtkApp) {
|
||||
int argc = 0;
|
||||
char** argv = nullptr;
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
m_gtkApp = gtk_application_new(nullptr, G_APPLICATION_FLAGS_NONE);
|
||||
}
|
||||
return new FileDialogGTK;
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,29 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
// Copyright (C) 2016 Gabriel Rauter
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_GTK_NATIVE_DIALOGS_H_INCLUDED
|
||||
#define OS_GTK_NATIVE_DIALOGS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/native_dialogs.h"
|
||||
|
||||
extern "C" struct _GtkApplication;
|
||||
|
||||
namespace os {
|
||||
|
||||
class NativeDialogsGTK : public NativeDialogs {
|
||||
public:
|
||||
NativeDialogsGTK();
|
||||
~NativeDialogsGTK();
|
||||
FileDialog* createFileDialog() override;
|
||||
private:
|
||||
_GtkApplication* m_gtkApp;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
165
src/os/keys.h
165
src/os/keys.h
@ -1,165 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_KEYS_H_INCLUDED
|
||||
#define OS_KEYS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
enum KeyModifiers {
|
||||
kKeyNoneModifier = 0,
|
||||
kKeyShiftModifier = 1,
|
||||
kKeyCtrlModifier = 2,
|
||||
kKeyAltModifier = 4,
|
||||
kKeyCmdModifier = 8,
|
||||
kKeySpaceModifier = 16,
|
||||
kKeyWinModifier = 32,
|
||||
kKeyUninitializedModifier = 64,
|
||||
};
|
||||
|
||||
// TODO These are virtual key code (not scancodes), we should rename
|
||||
// it to KeyCodes or use Unicode directly as on macOS (some
|
||||
// special keys like F1, arrow keys, etc. have a special
|
||||
// unicode value).
|
||||
enum KeyScancode {
|
||||
kKeyNil = 0,
|
||||
kKeyA = 1,
|
||||
kKeyB = 2,
|
||||
kKeyC = 3,
|
||||
kKeyD = 4,
|
||||
kKeyE = 5,
|
||||
kKeyF = 6,
|
||||
kKeyG = 7,
|
||||
kKeyH = 8,
|
||||
kKeyI = 9,
|
||||
kKeyJ = 10,
|
||||
kKeyK = 11,
|
||||
kKeyL = 12,
|
||||
kKeyM = 13,
|
||||
kKeyN = 14,
|
||||
kKeyO = 15,
|
||||
kKeyP = 16,
|
||||
kKeyQ = 17,
|
||||
kKeyR = 18,
|
||||
kKeyS = 19,
|
||||
kKeyT = 20,
|
||||
kKeyU = 21,
|
||||
kKeyV = 22,
|
||||
kKeyW = 23,
|
||||
kKeyX = 24,
|
||||
kKeyY = 25,
|
||||
kKeyZ = 26,
|
||||
kKey0 = 27,
|
||||
kKey1 = 28,
|
||||
kKey2 = 29,
|
||||
kKey3 = 30,
|
||||
kKey4 = 31,
|
||||
kKey5 = 32,
|
||||
kKey6 = 33,
|
||||
kKey7 = 34,
|
||||
kKey8 = 35,
|
||||
kKey9 = 36,
|
||||
kKey0Pad = 37,
|
||||
kKey1Pad = 38,
|
||||
kKey2Pad = 39,
|
||||
kKey3Pad = 40,
|
||||
kKey4Pad = 41,
|
||||
kKey5Pad = 42,
|
||||
kKey6Pad = 43,
|
||||
kKey7Pad = 44,
|
||||
kKey8Pad = 45,
|
||||
kKey9Pad = 46,
|
||||
kKeyF1 = 47,
|
||||
kKeyF2 = 48,
|
||||
kKeyF3 = 49,
|
||||
kKeyF4 = 50,
|
||||
kKeyF5 = 51,
|
||||
kKeyF6 = 52,
|
||||
kKeyF7 = 53,
|
||||
kKeyF8 = 54,
|
||||
kKeyF9 = 55,
|
||||
kKeyF10 = 56,
|
||||
kKeyF11 = 57,
|
||||
kKeyF12 = 58,
|
||||
kKeyEsc = 59,
|
||||
kKeyTilde = 60,
|
||||
kKeyMinus = 61,
|
||||
kKeyEquals = 62,
|
||||
kKeyBackspace = 63,
|
||||
kKeyTab = 64,
|
||||
kKeyOpenbrace = 65,
|
||||
kKeyClosebrace = 66,
|
||||
kKeyEnter = 67,
|
||||
kKeyColon = 68,
|
||||
kKeyQuote = 69,
|
||||
kKeyBackslash = 70,
|
||||
kKeyBackslash2 = 71,
|
||||
kKeyComma = 72,
|
||||
kKeyStop = 73,
|
||||
kKeySlash = 74,
|
||||
kKeySpace = 75,
|
||||
kKeyInsert = 76,
|
||||
kKeyDel = 77,
|
||||
kKeyHome = 78,
|
||||
kKeyEnd = 79,
|
||||
kKeyPageUp = 80,
|
||||
kKeyPageDown = 81,
|
||||
kKeyLeft = 82,
|
||||
kKeyRight = 83,
|
||||
kKeyUp = 84,
|
||||
kKeyDown = 85,
|
||||
kKeySlashPad = 86,
|
||||
kKeyAsterisk = 87,
|
||||
kKeyMinusPad = 88,
|
||||
kKeyPlusPad = 89,
|
||||
kKeyDelPad = 90,
|
||||
kKeyEnterPad = 91,
|
||||
kKeyPrtscr = 92,
|
||||
kKeyPause = 93,
|
||||
kKeyAbntC1 = 94,
|
||||
kKeyYen = 95,
|
||||
kKeyKana = 96,
|
||||
kKeyConvert = 97,
|
||||
kKeyNoconvert = 98,
|
||||
kKeyAt = 99,
|
||||
kKeyCircumflex = 100,
|
||||
kKeyColon2 = 101,
|
||||
kKeyKanji = 102,
|
||||
kKeyEqualsPad = 103, // macOS
|
||||
kKeyBackquote = 104, // macOS
|
||||
kKeySemicolon = 105, // macOS
|
||||
kKeyUnknown1 = 106,
|
||||
kKeyUnknown2 = 107,
|
||||
kKeyUnknown3 = 108,
|
||||
kKeyUnknown4 = 109,
|
||||
kKeyUnknown5 = 110,
|
||||
kKeyUnknown6 = 111,
|
||||
kKeyUnknown7 = 112,
|
||||
kKeyUnknown8 = 113,
|
||||
|
||||
kKeyFirstModifierScancode = 114,
|
||||
|
||||
kKeyLShift = 114,
|
||||
kKeyRShift = 115,
|
||||
kKeyLControl = 116,
|
||||
kKeyRControl = 117,
|
||||
kKeyAlt = 118,
|
||||
kKeyAltGr = 119,
|
||||
kKeyLWin = 120,
|
||||
kKeyRWin = 121,
|
||||
kKeyMenu = 122,
|
||||
kKeyCommand = 123,
|
||||
kKeyScrLock = 124,
|
||||
kKeyNumLock = 125,
|
||||
kKeyCapsLock = 126,
|
||||
|
||||
kKeyScancodes = 127
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,21 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_LOGGER_H_INCLUDED
|
||||
#define OS_LOGGER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
virtual ~Logger() { }
|
||||
virtual void logError(const char* error) = 0;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,92 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_MENUS_H_INCLUDED
|
||||
#define OS_MENUS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/keys.h"
|
||||
#include "os/shortcut.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace os {
|
||||
class MenuItem;
|
||||
|
||||
struct MenuItemInfo {
|
||||
enum Type {
|
||||
Normal,
|
||||
Separator
|
||||
};
|
||||
|
||||
enum Action {
|
||||
UserDefined,
|
||||
|
||||
// macOS standard commands
|
||||
Hide,
|
||||
HideOthers,
|
||||
ShowAll,
|
||||
Quit,
|
||||
Minimize,
|
||||
Zoom,
|
||||
};
|
||||
|
||||
Type type;
|
||||
Action action;
|
||||
std::string text;
|
||||
Shortcut shortcut;
|
||||
std::function<void()> execute;
|
||||
std::function<void(os::MenuItem*)> validate;
|
||||
|
||||
explicit MenuItemInfo(const Type type = Normal,
|
||||
const Action action = UserDefined)
|
||||
: type(type)
|
||||
, action(action) {
|
||||
}
|
||||
|
||||
explicit MenuItemInfo(const char* text,
|
||||
const Action action = UserDefined)
|
||||
: type(Normal)
|
||||
, action(action)
|
||||
, text(text) {
|
||||
}
|
||||
};
|
||||
|
||||
class Menu;
|
||||
class MenuItem;
|
||||
class Menus;
|
||||
|
||||
class MenuItem {
|
||||
public:
|
||||
virtual ~MenuItem() { }
|
||||
virtual void dispose() = 0;
|
||||
virtual void setText(const std::string& text) = 0;
|
||||
virtual void setSubmenu(Menu* submenu) = 0;
|
||||
virtual void setEnabled(bool state) = 0;
|
||||
virtual void setChecked(bool state) = 0;
|
||||
virtual void setShortcut(const Shortcut& shortcut) = 0;
|
||||
};
|
||||
|
||||
class Menu {
|
||||
public:
|
||||
virtual ~Menu() { }
|
||||
virtual void dispose() = 0;
|
||||
virtual void addItem(MenuItem* item) = 0;
|
||||
virtual void insertItem(const int index, MenuItem* item) = 0;
|
||||
};
|
||||
|
||||
class Menus {
|
||||
public:
|
||||
virtual ~Menus() { }
|
||||
virtual Menu* createMenu() = 0;
|
||||
virtual MenuItem* createMenuItem(const MenuItemInfo& info) = 0;
|
||||
virtual void setAppMenu(Menu* menu) = 0;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,39 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_NATIVE_CURSOR_H_INCLUDED
|
||||
#define OS_NATIVE_CURSOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
enum NativeCursor {
|
||||
kNoCursor,
|
||||
kArrowCursor,
|
||||
kCrosshairCursor,
|
||||
kIBeamCursor,
|
||||
kWaitCursor,
|
||||
kLinkCursor,
|
||||
kHelpCursor,
|
||||
kForbiddenCursor,
|
||||
kMoveCursor,
|
||||
kSizeNSCursor,
|
||||
kSizeWECursor,
|
||||
kSizeNCursor,
|
||||
kSizeNECursor,
|
||||
kSizeECursor,
|
||||
kSizeSECursor,
|
||||
kSizeSCursor,
|
||||
kSizeSWCursor,
|
||||
kSizeWCursor,
|
||||
kSizeNWCursor,
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_NATIVE_DIALOGS_H_INCLUDED
|
||||
#define OS_NATIVE_DIALOGS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/paths.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace os {
|
||||
class Display;
|
||||
|
||||
class FileDialog {
|
||||
public:
|
||||
enum class Type {
|
||||
OpenFile,
|
||||
OpenFiles,
|
||||
OpenFolder,
|
||||
SaveFile,
|
||||
};
|
||||
|
||||
virtual ~FileDialog() { }
|
||||
virtual void dispose() = 0;
|
||||
virtual void setType(const Type type) = 0;
|
||||
virtual void setTitle(const std::string& title) = 0;
|
||||
virtual void setDefaultExtension(const std::string& extension) = 0;
|
||||
virtual void addFilter(const std::string& extension, const std::string& description) = 0;
|
||||
virtual std::string fileName() = 0;
|
||||
virtual void getMultipleFileNames(base::paths& output) = 0;
|
||||
virtual void setFileName(const std::string& filename) = 0;
|
||||
virtual bool show(Display* parent) = 0;
|
||||
};
|
||||
|
||||
class NativeDialogs {
|
||||
public:
|
||||
virtual ~NativeDialogs() { }
|
||||
virtual FileDialog* createFileDialog() = 0;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,75 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "os/system.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class NoneSystem : public System {
|
||||
public:
|
||||
void dispose() override { delete this; }
|
||||
void activateApp() override { }
|
||||
void finishLaunching() override { }
|
||||
Capabilities capabilities() const override { return (Capabilities)0; }
|
||||
void useWintabAPI(bool enable) override { }
|
||||
Logger* logger() override { return nullptr; }
|
||||
Menus* menus() override { return nullptr; }
|
||||
NativeDialogs* nativeDialogs() override { return nullptr; }
|
||||
EventQueue* eventQueue() override { return nullptr; }
|
||||
bool gpuAcceleration() const override { return false; }
|
||||
void setGpuAcceleration(bool state) override { }
|
||||
gfx::Size defaultNewDisplaySize() override { return gfx::Size(0, 0); }
|
||||
Display* defaultDisplay() override { return nullptr; }
|
||||
Display* createDisplay(int width, int height, int scale) override { return nullptr; }
|
||||
Surface* createSurface(int width, int height) override { return nullptr; }
|
||||
Surface* createRgbaSurface(int width, int height) override { return nullptr; }
|
||||
Surface* loadSurface(const char* filename) override { return nullptr; }
|
||||
Surface* loadRgbaSurface(const char* filename) override { return nullptr; }
|
||||
Font* loadSpriteSheetFont(const char* filename, int scale) override { return nullptr; }
|
||||
Font* loadTrueTypeFont(const char* filename, int height) override { return nullptr; }
|
||||
bool isKeyPressed(KeyScancode scancode) override { return false; }
|
||||
KeyModifiers keyModifiers() override { return kKeyNoneModifier; }
|
||||
int getUnicodeFromScancode(KeyScancode scancode) override { return 0; }
|
||||
void setTranslateDeadKeys(bool state) override { }
|
||||
};
|
||||
|
||||
System* create_system_impl() {
|
||||
return new NoneSystem;
|
||||
}
|
||||
|
||||
void error_message(const char* msg)
|
||||
{
|
||||
fputs(msg, stderr);
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
|
||||
extern int app_main(int argc, char* argv[]);
|
||||
|
||||
#if _WIN32
|
||||
int wmain(int argc, wchar_t* wargv[], wchar_t* envp[]) {
|
||||
char** argv;
|
||||
if (wargv && argc > 0) {
|
||||
argv = new char*[argc];
|
||||
for (int i=0; i<argc; ++i)
|
||||
argv[i] = base_strdup(base::to_utf8(std::wstring(wargv[i])).c_str());
|
||||
}
|
||||
else {
|
||||
argv = new char*[1];
|
||||
argv[0] = base_strdup("");
|
||||
argc = 1;
|
||||
}
|
||||
#else
|
||||
int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
return app_main(argc, argv);
|
||||
}
|
31
src/os/os.h
31
src/os/os.h
@ -1,31 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_H_INCLUDED
|
||||
#define OS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/capabilities.h"
|
||||
#include "os/display.h"
|
||||
#include "os/display_handle.h"
|
||||
#include "os/draw_text.h"
|
||||
#include "os/error.h"
|
||||
#include "os/event.h"
|
||||
#include "os/event_queue.h"
|
||||
#include "os/font.h"
|
||||
#include "os/keys.h"
|
||||
#include "os/logger.h"
|
||||
#include "os/menus.h"
|
||||
#include "os/native_cursor.h"
|
||||
#include "os/native_dialogs.h"
|
||||
#include "os/pointer_type.h"
|
||||
#include "os/scoped_handle.h"
|
||||
#include "os/shortcut.h"
|
||||
#include "os/surface.h"
|
||||
#include "os/surface_format.h"
|
||||
#include "os/system.h"
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_APP_H_INCLUDED
|
||||
#define OS_OSX_APP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
class thread;
|
||||
}
|
||||
|
||||
namespace os {
|
||||
|
||||
class OSXApp {
|
||||
public:
|
||||
static OSXApp* instance();
|
||||
|
||||
OSXApp();
|
||||
~OSXApp();
|
||||
|
||||
bool init();
|
||||
void activateApp();
|
||||
void finishLaunching();
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* m_impl;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,95 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "os/osx/app.h"
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "base/thread.h"
|
||||
#include "os/osx/app_delegate.h"
|
||||
|
||||
extern int app_main(int argc, char* argv[]);
|
||||
|
||||
namespace os {
|
||||
|
||||
class OSXApp::Impl {
|
||||
public:
|
||||
bool init() {
|
||||
m_app = [NSApplication sharedApplication];
|
||||
m_appDelegate = [OSXAppDelegate new];
|
||||
|
||||
[m_app setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
[m_app setDelegate:m_appDelegate];
|
||||
|
||||
// Don't activate the application ignoring other apps. This is
|
||||
// called by OS X when the application is launched by the user
|
||||
// from the application bundle. In this way, we can execute
|
||||
// aseprite from the command line/bash scripts and the app will
|
||||
// not be activated.
|
||||
//[m_app activateIgnoringOtherApps:YES];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We might need to call this function when the app is launched from
|
||||
// Steam. It appears that there is a bug on OS X Steam client where
|
||||
// the app is launched, activated, and then the Steam client is
|
||||
// activated again.
|
||||
void activateApp() {
|
||||
[m_app activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
void finishLaunching() {
|
||||
[m_app finishLaunching];
|
||||
}
|
||||
|
||||
private:
|
||||
NSApplication* m_app;
|
||||
OSXAppDelegate* m_appDelegate;
|
||||
};
|
||||
|
||||
static OSXApp* g_instance = nullptr;
|
||||
|
||||
// static
|
||||
OSXApp* OSXApp::instance()
|
||||
{
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
OSXApp::OSXApp()
|
||||
: m_impl(new Impl)
|
||||
{
|
||||
ASSERT(!g_instance);
|
||||
g_instance = this;
|
||||
}
|
||||
|
||||
OSXApp::~OSXApp()
|
||||
{
|
||||
ASSERT(g_instance == this);
|
||||
g_instance = nullptr;
|
||||
}
|
||||
|
||||
bool OSXApp::init()
|
||||
{
|
||||
return m_impl->init();
|
||||
}
|
||||
|
||||
void OSXApp::activateApp()
|
||||
{
|
||||
m_impl->activateApp();
|
||||
}
|
||||
|
||||
void OSXApp::finishLaunching()
|
||||
{
|
||||
m_impl->finishLaunching();
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,24 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_APP_DELEGATE_H_INCLUDED
|
||||
#define OS_OSX_APP_DELEGATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
@interface OSXAppDelegate : NSObject<NSApplicationDelegate>
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app;
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification;
|
||||
- (void)applicationWillResignActive:(NSNotification*)notification;
|
||||
- (void)applicationDidBecomeActive:(NSNotification*)notification;
|
||||
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames;
|
||||
- (void)executeMenuItem:(id)sender;
|
||||
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem;
|
||||
@end
|
||||
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "os/osx/app_delegate.h"
|
||||
|
||||
#include "base/fs.h"
|
||||
#include "os/event.h"
|
||||
#include "os/event_queue.h"
|
||||
#include "os/osx/app.h"
|
||||
#include "os/osx/generate_drop_files.h"
|
||||
#include "os/osx/view.h"
|
||||
#include "os/system.h"
|
||||
|
||||
@protocol OSXValidateMenuItemProtocol
|
||||
- (void)validateMenuItem;
|
||||
@end
|
||||
|
||||
@implementation OSXAppDelegate
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
|
||||
{
|
||||
os::Event ev;
|
||||
ev.setType(os::Event::CloseDisplay);
|
||||
os::queue_event(ev);
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification
|
||||
{
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification*)notification
|
||||
{
|
||||
NSEvent* event = [NSApp currentEvent];
|
||||
if (event != nil)
|
||||
[OSXView updateKeyFlags:event];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification*)notification
|
||||
{
|
||||
NSEvent* event = [NSApp currentEvent];
|
||||
if (event != nil)
|
||||
[OSXView updateKeyFlags:event];
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames
|
||||
{
|
||||
generate_drop_files_from_nsarray(filenames);
|
||||
|
||||
[app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)executeMenuItem:(id)sender
|
||||
{
|
||||
[sender executeMenuItem:sender];
|
||||
}
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
|
||||
{
|
||||
if ([menuItem respondsToSelector:@selector(validateMenuItem)]) {
|
||||
[((id<OSXValidateMenuItemProtocol>)menuItem) validateMenuItem];
|
||||
return menuItem.enabled;
|
||||
}
|
||||
else
|
||||
return [super validateMenuItem:menuItem];
|
||||
}
|
||||
|
||||
@end
|
@ -1,30 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_EVENT_QUEUE_INCLUDED
|
||||
#define OS_OSX_EVENT_QUEUE_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/concurrent_queue.h"
|
||||
#include "os/event.h"
|
||||
#include "os/event_queue.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class OSXEventQueue : public EventQueue {
|
||||
public:
|
||||
void getEvent(Event& ev, bool canWait) override;
|
||||
void queueEvent(const Event& ev) override;
|
||||
|
||||
private:
|
||||
base::concurrent_queue<Event> m_events;
|
||||
};
|
||||
|
||||
typedef OSXEventQueue EventQueueImpl;
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,67 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include "os/osx/event_queue.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
void OSXEventQueue::getEvent(Event& ev, bool canWait)
|
||||
{
|
||||
ev.setType(Event::None);
|
||||
|
||||
@autoreleasepool {
|
||||
retry:;
|
||||
NSApplication* app = [NSApplication sharedApplication];
|
||||
if (!app)
|
||||
return;
|
||||
|
||||
// Pump the whole queue of Cocoa events
|
||||
NSEvent* event;
|
||||
do {
|
||||
event = [app nextEventMatchingMask:NSEventMaskAny
|
||||
untilDate:[NSDate distantPast]
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (event) {
|
||||
// Intercept <Control+Tab>, <Cmd+[>, and other keyboard
|
||||
// combinations, and send them directly to the main
|
||||
// NSView. Without this, the NSApplication intercepts the key
|
||||
// combination and use it to go to the next key view.
|
||||
if (event.type == NSEventTypeKeyDown) {
|
||||
[app.mainWindow.contentView keyDown:event];
|
||||
}
|
||||
else {
|
||||
[app sendEvent:event];
|
||||
}
|
||||
}
|
||||
} while (event);
|
||||
|
||||
if (!m_events.try_pop(ev)) {
|
||||
if (canWait) {
|
||||
// Wait until there is a Cocoa event in queue
|
||||
[NSApp nextEventMatchingMask:NSEventMaskAny
|
||||
untilDate:[NSDate distantFuture]
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:NO];
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSXEventQueue::queueEvent(const Event& ev)
|
||||
{
|
||||
m_events.push(ev);
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,27 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_APP_GENERATE_DROP_FILES_H_INCLUDED
|
||||
#define OS_OSX_APP_GENERATE_DROP_FILES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/fs.h"
|
||||
|
||||
inline void generate_drop_files_from_nsarray(NSArray* filenames)
|
||||
{
|
||||
base::paths files;
|
||||
for (int i=0; i<[filenames count]; ++i) {
|
||||
NSString* fn = [filenames objectAtIndex: i];
|
||||
files.push_back(base::normalize_path([fn UTF8String]));
|
||||
}
|
||||
|
||||
os::Event ev;
|
||||
ev.setType(os::Event::DropFiles);
|
||||
ev.setFiles(files);
|
||||
os::queue_event(ev);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,25 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_KEYS_H_INCLUDED
|
||||
#define OS_OSX_KEYS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/keys.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
namespace os {
|
||||
|
||||
KeyScancode scancode_from_nsevent(NSEvent* event);
|
||||
|
||||
CFStringRef get_unicode_from_key_code(const UInt16 keyCode,
|
||||
const NSEventModifierFlags modifierFlags,
|
||||
UInt32* deadKeyState = nullptr);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,433 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
// Uncomment this to log how scancodes are generated
|
||||
#define KEY_TRACE(...)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/osx/keys.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h> // TIS functions
|
||||
|
||||
namespace os {
|
||||
|
||||
static KeyScancode from_char_to_scancode(int chr)
|
||||
{
|
||||
static KeyScancode map[] = {
|
||||
kKeyNil, // 0 = 00 = NUL
|
||||
kKeyNil, // 1 = 01 = STX
|
||||
kKeyNil, // 2 = 02 = SOT
|
||||
kKeyNil, // 3 = 03 = ETX
|
||||
kKeyNil, // 4 = 04 = EOT
|
||||
kKeyNil, // 5 = 05 = ENQ
|
||||
kKeyNil, // 6 = 06 = ACK
|
||||
kKeyNil, // 7 = 07 = BEL
|
||||
kKeyBackspace, // 8 = 08 = BS
|
||||
kKeyNil, // 9 = 09 = HT
|
||||
kKeyNil, // 10 =0A = LF
|
||||
kKeyNil, // 11 =0B = VT
|
||||
kKeyNil, // 12 =0C = FF
|
||||
kKeyNil, // 13 =0D = CR
|
||||
kKeyNil, // 14 =0E = SO
|
||||
kKeyNil, // 15 =0F = SI
|
||||
kKeyNil, // 16 =10 = DLE
|
||||
kKeyNil, // 17 =11 = DC1
|
||||
kKeyNil, // 18 =12 = DC2
|
||||
kKeyNil, // 19 =13 = DC3
|
||||
kKeyNil, // 20 =14 = DC4
|
||||
kKeyNil, // 21 =15 = NAK
|
||||
kKeyNil, // 22 =16 = SYN
|
||||
kKeyNil, // 23 =17 = ETB
|
||||
kKeyNil, // 24 =18 = CAN
|
||||
kKeyNil, // 25 =19 = EM
|
||||
kKeyNil, // 26 =1A = SUB
|
||||
kKeyNil, // 27 =1B = ESC
|
||||
kKeyNil, // 28 =1C = FS
|
||||
kKeyNil, // 29 =1D = GS
|
||||
kKeyNil, // 30 =1E = RS
|
||||
kKeyNil, // 31 =1F = US
|
||||
kKeySpace, // 32 =20 = Space
|
||||
kKeyNil, // 33 =21 = !
|
||||
kKeyNil, // 34 =22 = "
|
||||
kKeyNil, // 35 =23 = #
|
||||
kKeyNil, // 36 =24 = $
|
||||
kKeyNil, // 37 =25 = %
|
||||
kKeyNil, // 38 =26 = &
|
||||
kKeyQuote, // 39 =27 = '
|
||||
kKeyNil, // 40 = 28 = (
|
||||
kKeyNil, // 41 = 29 = )
|
||||
kKeyNil, // 42 = 2A = *
|
||||
kKeyNil, // 43 = 2B = +
|
||||
kKeyComma, // 44 = 2C = ,
|
||||
kKeyMinus, // 45 = 2D = -
|
||||
kKeyStop, // 46 = 2E = .
|
||||
kKeySlash, // 47 = 2F = /
|
||||
kKey0, // 48 = 30 = 0
|
||||
kKey1, // 49 = 31 = 1
|
||||
kKey2, // 50 = 32 = 2
|
||||
kKey3, // 51 = 33 = 3
|
||||
kKey4, // 52 = 34 = 4
|
||||
kKey5, // 53 = 35 = 5
|
||||
kKey6, // 54 = 36 = 6
|
||||
kKey7, // 55 = 37 = 7
|
||||
kKey8, // 56 = 38 = 8
|
||||
kKey9, // 57 = 39 = 9
|
||||
kKeyColon, // 58 = 3A = :
|
||||
kKeySemicolon, // 59 = 3B = ;
|
||||
kKeyNil, // 60 = 3C = <
|
||||
kKeyEquals, // 61 = 3D = =
|
||||
kKeyNil, // 62 = 3E = >
|
||||
kKeyNil, // 63 = 3F = ?
|
||||
kKeyNil, // 64 = 40 = @
|
||||
kKeyA, // 65 = 41 = A
|
||||
kKeyB, // 66 = 42 = B
|
||||
kKeyC, // 67 = 43 = C
|
||||
kKeyD, // 68 = 44 = D
|
||||
kKeyE, // 69 = 45 = E
|
||||
kKeyF, // 70 = 46 = F
|
||||
kKeyG, // 71 = 47 = G
|
||||
kKeyH, // 72 = 48 = H
|
||||
kKeyI, // 73 = 49 = I
|
||||
kKeyJ, // 74 = 4A = J
|
||||
kKeyK, // 75 = 4B = K
|
||||
kKeyL, // 76 = 4C = L
|
||||
kKeyM, // 77 = 4D = M
|
||||
kKeyN, // 78 = 4E = N
|
||||
kKeyO, // 79 = 4F = O
|
||||
kKeyP, // 80 = 50 = P
|
||||
kKeyQ, // 81 = 51 = Q
|
||||
kKeyR, // 82 = 52 = R
|
||||
kKeyS, // 83 = 53 = S
|
||||
kKeyT, // 84 = 54 = T
|
||||
kKeyU, // 85 = 55 = U
|
||||
kKeyV, // 86 = 56 = V
|
||||
kKeyW, // 87 = 57 = W
|
||||
kKeyX, // 88 = 58 = X
|
||||
kKeyY, // 89 = 59 = Y
|
||||
kKeyZ, // 90 = 5A = Z
|
||||
kKeyOpenbrace, // 91 = 5B = [
|
||||
kKeyBackslash, // 92 = 5C = backslash
|
||||
kKeyClosebrace, // 93 = 5D = ]
|
||||
kKeyCircumflex, // 94 = 5E = ^
|
||||
kKeyNil, // 95 = 5F = _
|
||||
kKeyBackquote, // 96 = 60 = `
|
||||
kKeyA, // 97 = 61 = a
|
||||
kKeyB, // 98 = 62 = b
|
||||
kKeyC, // 99 = 63 = c
|
||||
kKeyD, // 100 = 64 = d
|
||||
kKeyE, // 101 = 65 = e
|
||||
kKeyF, // 102 = 66 = f
|
||||
kKeyG, // 103 = 67 = g
|
||||
kKeyH, // 104 = 68 = h
|
||||
kKeyI, // 105 = 69 = i
|
||||
kKeyJ, // 106 = 6A = j
|
||||
kKeyK, // 107 = 6B = k
|
||||
kKeyL, // 108 = 6C = l
|
||||
kKeyM, // 109 = 6D = m
|
||||
kKeyN, // 110 = 6E = n
|
||||
kKeyO, // 111 = 6F = o
|
||||
kKeyP, // 112 = 70 = p
|
||||
kKeyQ, // 113 = 71 = q
|
||||
kKeyR, // 114 = 72 = r
|
||||
kKeyS, // 115 = 73 = s
|
||||
kKeyT, // 116 = 74 = t
|
||||
kKeyU, // 117 = 75 = u
|
||||
kKeyV, // 118 = 76 = v
|
||||
kKeyW, // 119 = 77 = w
|
||||
kKeyX, // 120 = 78 = x
|
||||
kKeyY, // 121 = 79 = y
|
||||
kKeyZ, // 122 = 7A = z
|
||||
kKeyOpenbrace, // 123 = 7B = {
|
||||
kKeyBackslash, // 124 = 7C = |
|
||||
kKeyClosebrace, // 125 = 7D = }
|
||||
kKeyTilde, // 126 = 7E = ~
|
||||
kKeyNil, // 127 = 7F = DEL
|
||||
};
|
||||
|
||||
if (chr >= 0 && chr < sizeof(map) / sizeof(map[0])) {
|
||||
// Converts an ASCII character into a os::KeyScancode
|
||||
return map[chr];
|
||||
}
|
||||
else
|
||||
return kKeyNil;
|
||||
}
|
||||
|
||||
static KeyScancode from_keycode_to_scancode(UInt16 keyCode)
|
||||
{
|
||||
// Converts macOS virtual key code into a os::KeyScancode
|
||||
static KeyScancode map[256] = {
|
||||
// 0x00
|
||||
kKeyA, // 0x00 - kVK_ANSI_A
|
||||
kKeyS, // 0x01 - kVK_ANSI_S
|
||||
kKeyD, // 0x02 - kVK_ANSI_D
|
||||
kKeyF, // 0x03 - kVK_ANSI_F
|
||||
kKeyH, // 0x04 - kVK_ANSI_H
|
||||
kKeyG, // 0x05 - kVK_ANSI_G
|
||||
kKeyZ, // 0x06 - kVK_ANSI_Z
|
||||
kKeyX, // 0x07 - kVK_ANSI_X
|
||||
kKeyC, // 0x08 - kVK_ANSI_C
|
||||
kKeyV, // 0x09 - kVK_ANSI_V
|
||||
kKeyNil, // 0x0A - kVK_ISO_Section
|
||||
kKeyB, // 0x0B - kVK_ANSI_B
|
||||
kKeyQ, // 0x0C - kVK_ANSI_Q
|
||||
kKeyW, // 0x0D - kVK_ANSI_W
|
||||
kKeyE, // 0x0E - kVK_ANSI_E
|
||||
kKeyR, // 0x0F - kVK_ANSI_R
|
||||
// 0x10
|
||||
kKeyY, // 0x10 - kVK_ANSI_Y
|
||||
kKeyT, // 0x11 - kVK_ANSI_T
|
||||
kKey1, // 0x12 - kVK_ANSI_1
|
||||
kKey2, // 0x13 - kVK_ANSI_2
|
||||
kKey3, // 0x14 - kVK_ANSI_3
|
||||
kKey4, // 0x15 - kVK_ANSI_4
|
||||
kKey6, // 0x16 - kVK_ANSI_6
|
||||
kKey5, // 0x17 - kVK_ANSI_5
|
||||
kKeyEquals, // 0x18 - kVK_ANSI_Equal
|
||||
kKey9, // 0x19 - kVK_ANSI_9
|
||||
kKey7, // 0x1A - kVK_ANSI_7
|
||||
kKeyMinus, // 0x1B - kVK_ANSI_Minus
|
||||
kKey8, // 0x1C - kVK_ANSI_8
|
||||
kKey0, // 0x1D - kVK_ANSI_0
|
||||
kKeyClosebrace, // 0x1E - kVK_ANSI_RightBracket
|
||||
kKeyO, // 0x1F - kVK_ANSI_O
|
||||
// 0x20
|
||||
kKeyU, // 0x20 - kVK_ANSI_U
|
||||
kKeyOpenbrace, // 0x21 - kVK_ANSI_LeftBracket
|
||||
kKeyI, // 0x22 - kVK_ANSI_I
|
||||
kKeyP, // 0x23 - kVK_ANSI_P
|
||||
kKeyEnter, // 0x24 - kVK_Return
|
||||
kKeyL, // 0x25 - kVK_ANSI_L
|
||||
kKeyJ, // 0x26 - kVK_ANSI_J
|
||||
kKeyQuote, // 0x27 - kVK_ANSI_Quote
|
||||
kKeyK, // 0x28 - kVK_ANSI_K
|
||||
kKeySemicolon, // 0x29 - kVK_ANSI_Semicolon
|
||||
kKeyBackslash, // 0x2A - kVK_ANSI_Backslash
|
||||
kKeyComma, // 0x2B - kVK_ANSI_Comma
|
||||
kKeySlash, // 0x2C - kVK_ANSI_Slash
|
||||
kKeyN, // 0x2D - kVK_ANSI_N
|
||||
kKeyM, // 0x2E - kVK_ANSI_M
|
||||
kKeyStop, // 0x2F - kVK_ANSI_Period
|
||||
// 0x30
|
||||
kKeyTab, // 0x30 - kVK_Tab
|
||||
kKeySpace, // 0x31 - kVK_Space
|
||||
kKeyNil, // 0x32 - kVK_ANSI_Grave
|
||||
kKeyBackspace, // 0x33 - kVK_Delete
|
||||
kKeyNil, // 0x34 - ?
|
||||
kKeyEsc, // 0x35 - kVK_Escape
|
||||
kKeyNil, // 0x36 - ?
|
||||
kKeyCommand, // 0x37 - kVK_Command
|
||||
kKeyLShift, // 0x38 - kVK_Shift
|
||||
kKeyCapsLock, // 0x39 - kVK_CapsLock
|
||||
kKeyAlt, // 0x3A - kVK_Option
|
||||
kKeyLControl, // 0x3B - kVK_Control
|
||||
kKeyRShift, // 0x3C - kVK_RightShift
|
||||
kKeyAltGr, // 0x3D - kVK_RightOption
|
||||
kKeyRControl, // 0x3E - kVK_RightControl
|
||||
kKeyNil, // 0x3F - kVK_Function
|
||||
// 0x40
|
||||
kKeyNil, // 0x40 - kVK_F17
|
||||
kKeyNil, // 0x41 - kVK_ANSI_KeypadDecimal
|
||||
kKeyNil, // 0x42 - ?
|
||||
kKeyAsterisk, // 0x43 - kVK_ANSI_KeypadMultiply
|
||||
kKeyNil, // 0x44 - ?
|
||||
kKeyPlusPad, // 0x45 - kVK_ANSI_KeypadPlus
|
||||
kKeyNil, // 0x46 - ?
|
||||
kKeyDelPad, // 0x47 - kVK_ANSI_KeypadClear
|
||||
kKeyNil, // 0x48 - kVK_VolumeUp
|
||||
kKeyNil, // 0x49 - kVK_VolumeDown
|
||||
kKeyNil, // 0x4A - kVK_Mute
|
||||
kKeySlashPad, // 0x4B - kVK_ANSI_KeypadDivide
|
||||
kKeyEnterPad, // 0x4C - kVK_ANSI_KeypadEnter
|
||||
kKeyNil, // 0x4D - ?
|
||||
kKeyMinusPad, // 0x4E - kVK_ANSI_KeypadMinus
|
||||
kKeyNil, // 0x4F - kVK_F18
|
||||
// 0x50
|
||||
kKeyNil, // 0x50 - kVK_F19
|
||||
kKeyEqualsPad, // 0x51 - kVK_ANSI_KeypadEquals
|
||||
kKey0Pad, // 0x52 - kVK_ANSI_Keypad0
|
||||
kKey1Pad, // 0x53 - kVK_ANSI_Keypad1
|
||||
kKey2Pad, // 0x54 - kVK_ANSI_Keypad2
|
||||
kKey3Pad, // 0x55 - kVK_ANSI_Keypad3
|
||||
kKey4Pad, // 0x56 - kVK_ANSI_Keypad4
|
||||
kKey5Pad, // 0x57 - kVK_ANSI_Keypad5
|
||||
kKey6Pad, // 0x58 - kVK_ANSI_Keypad6
|
||||
kKey7Pad, // 0x59 - kVK_ANSI_Keypad7
|
||||
kKeyNil, // 0x5A - kVK_F20
|
||||
kKey8Pad, // 0x5B - kVK_ANSI_Keypad8
|
||||
kKey9Pad, // 0x5C - kVK_ANSI_Keypad9
|
||||
kKeyYen, // 0x5D - kVK_JIS_Yen
|
||||
kKeyNil, // 0x5E - kVK_JIS_Underscore
|
||||
kKeyNil, // 0x5F - kVK_JIS_KeypadComma
|
||||
// 0x60
|
||||
kKeyF5, // 0x60 - kVK_F5
|
||||
kKeyF6, // 0x61 - kVK_F6
|
||||
kKeyF7, // 0x62 - kVK_F7
|
||||
kKeyF3, // 0x63 - kVK_F3
|
||||
kKeyF8, // 0x64 - kVK_F8
|
||||
kKeyF9, // 0x65 - kVK_F9
|
||||
kKeyNil, // 0x66 - kVK_JIS_Eisu
|
||||
kKeyF11, // 0x67 - kVK_F11
|
||||
kKeyKana, // 0x68 - kVK_JIS_Kana
|
||||
kKeyNil, // 0x69 - kVK_F13
|
||||
kKeyNil, // 0x6A - kVK_F16
|
||||
kKeyNil, // 0x6B - kVK_F14
|
||||
kKeyNil, // 0x6C - ?
|
||||
kKeyF10, // 0x6D - kVK_F10
|
||||
kKeyNil, // 0x6E - ?
|
||||
kKeyF12, // 0x6F - kVK_F12
|
||||
// 0x70
|
||||
kKeyNil, // 0x70 - ?
|
||||
kKeyNil, // 0x71 - kVK_F15
|
||||
kKeyNil, // 0x72 - kVK_Help
|
||||
kKeyHome, // 0x73 - kVK_Home
|
||||
kKeyPageUp, // 0x74 - kVK_PageUp
|
||||
kKeyDel, // 0x75 - kVK_ForwardDelete
|
||||
kKeyF4, // 0x76 - kVK_F4
|
||||
kKeyEnd, // 0x77 - kVK_End
|
||||
kKeyF2, // 0x78 - kVK_F2
|
||||
kKeyPageDown, // 0x79 - kVK_PageDown
|
||||
kKeyF1, // 0x7A - kVK_F1
|
||||
kKeyLeft, // 0x7B - kVK_LeftArrow
|
||||
kKeyRight, // 0x7C - kVK_RightArrow
|
||||
kKeyDown, // 0x7D - kVK_DownArrow
|
||||
kKeyUp, // 0x7E - kVK_UpArrow
|
||||
kKeyNil // 0x7F - ?
|
||||
};
|
||||
|
||||
if (keyCode >= 0 && keyCode < sizeof(map) / sizeof(map[0])) {
|
||||
// Converts macOS virtual key into a os::KeyScancode
|
||||
return map[keyCode];
|
||||
}
|
||||
else
|
||||
return kKeyNil;
|
||||
}
|
||||
|
||||
KeyScancode scancode_from_nsevent(NSEvent* event)
|
||||
{
|
||||
#if 1
|
||||
// For keys that are not in the numpad we try to get the scancode
|
||||
// converting the first char in NSEvent.characters to a
|
||||
// scancode.
|
||||
if ((event.modifierFlags & NSEventModifierFlagNumericPad) == 0) {
|
||||
KeyScancode code;
|
||||
|
||||
// It looks like getting the first "event.characters" char is the
|
||||
// only way to get the correct Cmd+letter combination on "Dvorak -
|
||||
// QWERTY Cmd" keyboard layout.
|
||||
NSString* chars = event.characters;
|
||||
if (chars.length > 0) {
|
||||
int chr = [chars characterAtIndex:chars.length-1];
|
||||
|
||||
// Avoid activating space bar modifier. E.g. pressing
|
||||
// Ctrl+Alt+Shift+S on "Spanish ISO" layout generates a
|
||||
// whitespace ' ', and we prefer the S scancode instead of the
|
||||
// space bar scancode.
|
||||
if (chr != 32) {
|
||||
code = from_char_to_scancode(chr);
|
||||
if (code != kKeyNil) {
|
||||
KEY_TRACE("scancode_from_nsevent %d -> %d (characters)\n",
|
||||
(int)chr, (int)code);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chars = event.charactersIgnoringModifiers;
|
||||
if (chars.length > 0) {
|
||||
int chr = [chars characterAtIndex:chars.length-1];
|
||||
code = from_char_to_scancode(chr);
|
||||
if (code != kKeyNil) {
|
||||
KEY_TRACE("scancode_from_nsevent %d -> %d (charactersIgnoringModifiers)\n",
|
||||
(int)chr, (int)code);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // Don't use this code, it reports scancodes always as QWERTY
|
||||
// and doesn't work for Dvorak or AZERTY layouts.
|
||||
{
|
||||
CFStringRef strRef = get_unicode_from_key_code(
|
||||
event.keyCode,
|
||||
event.modifierFlags & NSCommandKeyMask);
|
||||
|
||||
if (strRef) {
|
||||
KeyScancode code = kKeyNil;
|
||||
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length > 0) {
|
||||
// Converts the first unicode char into a macOS virtual key
|
||||
UInt16 chr = CFStringGetCharacterAtIndex(strRef, length-1);
|
||||
code = from_char_to_scancode(chr);
|
||||
if (code != kKeyNil) {
|
||||
KEY_TRACE("scancode_from_nsevent %d -> %d (get_unicode_from_key_code)\n",
|
||||
(int)chr, (int)code);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(strRef);
|
||||
if (code != kKeyNil) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
KeyScancode code = from_keycode_to_scancode(event.keyCode);
|
||||
KEY_TRACE("scancode_from_nsevent %d -> %d (keyCode)\n",
|
||||
(int)event.keyCode, (int)code);
|
||||
return code;
|
||||
}
|
||||
|
||||
// Based on code from:
|
||||
// http://stackoverflow.com/questions/22566665/how-to-capture-unicode-from-key-events-without-an-nstextview
|
||||
// http://stackoverflow.com/questions/12547007/convert-key-code-into-key-equivalent-string
|
||||
// http://stackoverflow.com/questions/8263618/convert-virtual-key-code-to-unicode-string
|
||||
//
|
||||
// If "deadKeyState" is = nullptr, it doesn't process dead keys.
|
||||
CFStringRef get_unicode_from_key_code(const UInt16 keyCode,
|
||||
const NSEventModifierFlags modifierFlags,
|
||||
UInt32* deadKeyState)
|
||||
{
|
||||
// The "TISCopyCurrentKeyboardInputSource()" doesn't contain
|
||||
// kTISPropertyUnicodeKeyLayoutData (returns nullptr) when the input
|
||||
// source is Japanese (Romaji/Hiragana/Katakana).
|
||||
|
||||
//TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
|
||||
TISInputSourceRef inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
CFDataRef keyLayoutData = (CFDataRef)TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData);
|
||||
const UCKeyboardLayout* keyLayout =
|
||||
(keyLayoutData ? (const UCKeyboardLayout*)CFDataGetBytePtr(keyLayoutData): nullptr);
|
||||
|
||||
UInt32 deadKeyStateWrap = (deadKeyState ? *deadKeyState: 0);
|
||||
UniChar output[4];
|
||||
UniCharCount length;
|
||||
|
||||
// Reference here:
|
||||
// https://developer.apple.com/reference/coreservices/1390584-uckeytranslate?language=objc
|
||||
UCKeyTranslate(
|
||||
keyLayout,
|
||||
keyCode,
|
||||
kUCKeyActionDown,
|
||||
((modifierFlags >> 16) & 0xFF),
|
||||
LMGetKbdType(),
|
||||
(deadKeyState ? 0: kUCKeyTranslateNoDeadKeysMask),
|
||||
&deadKeyStateWrap,
|
||||
sizeof(output) / sizeof(output[0]),
|
||||
&length,
|
||||
output);
|
||||
|
||||
if (deadKeyState)
|
||||
*deadKeyState = deadKeyStateWrap;
|
||||
|
||||
CFRelease(inputSource);
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, output, length);
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,25 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "os/logger.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class OSXLogger : public Logger {
|
||||
public:
|
||||
void logError(const char* error) override {
|
||||
NSLog([NSString stringWithUTF8String:error]);
|
||||
}
|
||||
};
|
||||
|
||||
Logger* getOsxLogger() {
|
||||
return new OSXLogger;
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,25 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_MENUS_H_INCLUDED
|
||||
#define OS_OSX_MENUS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/menus.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class MenusOSX : public Menus {
|
||||
public:
|
||||
MenusOSX();
|
||||
Menu* createMenu() override;
|
||||
MenuItem* createMenuItem(const MenuItemInfo& info) override;
|
||||
void setAppMenu(Menu* menu) override;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,288 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "base/string.h"
|
||||
#include "os/osx/menus.h"
|
||||
#include "os/shortcut.h"
|
||||
|
||||
namespace os {
|
||||
class MenuItemOSX;
|
||||
}
|
||||
|
||||
@interface OSXNSMenu : NSMenu
|
||||
- (BOOL)performKeyEquivalent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
@interface OSXNSMenuItem : NSMenuItem {
|
||||
os::MenuItemOSX* original;
|
||||
}
|
||||
+ (OSXNSMenuItem*)alloc:(os::MenuItemOSX*)original;
|
||||
- (void)executeMenuItem:(id)sender;
|
||||
- (void)validateMenuItem;
|
||||
@end
|
||||
|
||||
namespace os {
|
||||
|
||||
extern bool g_keyEquivalentUsed;
|
||||
|
||||
class MenuItemOSX : public MenuItem {
|
||||
public:
|
||||
MenuItemOSX(const MenuItemInfo& info);
|
||||
void dispose() override;
|
||||
void setText(const std::string& text) override;
|
||||
void setSubmenu(Menu* submenu) override;
|
||||
void setEnabled(bool state) override;
|
||||
void setChecked(bool state) override;
|
||||
void setShortcut(const Shortcut& shortcut) override;
|
||||
NSMenuItem* handle() { return m_handle; }
|
||||
|
||||
// Called by OSXNSMenuItem.executeMenuItem
|
||||
void execute();
|
||||
void validate();
|
||||
|
||||
private:
|
||||
void syncTitle();
|
||||
NSMenuItem* m_handle;
|
||||
Menu* m_submenu;
|
||||
std::function<void()> m_execute;
|
||||
std::function<void(os::MenuItem*)> m_validate;
|
||||
};
|
||||
|
||||
class MenuOSX : public Menu {
|
||||
public:
|
||||
MenuOSX();
|
||||
void dispose() override;
|
||||
void addItem(MenuItem* item) override;
|
||||
void insertItem(const int index, MenuItem* item) override;
|
||||
NSMenu* handle() { return m_handle; }
|
||||
private:
|
||||
NSMenu* m_handle;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
@implementation OSXNSMenu
|
||||
- (BOOL)performKeyEquivalent:(NSEvent*)event
|
||||
{
|
||||
BOOL result = [super performKeyEquivalent:event];
|
||||
if (result)
|
||||
os::g_keyEquivalentUsed = true;
|
||||
return result;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation OSXNSMenuItem
|
||||
+ (OSXNSMenuItem*)alloc:(os::MenuItemOSX*)original
|
||||
{
|
||||
OSXNSMenuItem* item = [super alloc];
|
||||
item->original = original;
|
||||
return item;
|
||||
}
|
||||
- (void)executeMenuItem:(id)sender
|
||||
{
|
||||
original->execute();
|
||||
}
|
||||
- (void)validateMenuItem
|
||||
{
|
||||
original->validate();
|
||||
}
|
||||
@end
|
||||
|
||||
namespace os {
|
||||
|
||||
MenuItemOSX::MenuItemOSX(const MenuItemInfo& info)
|
||||
: m_handle(nullptr)
|
||||
, m_submenu(nullptr)
|
||||
{
|
||||
switch (info.type) {
|
||||
|
||||
case MenuItemInfo::Normal: {
|
||||
SEL sel = nil;
|
||||
id target = nil;
|
||||
switch (info.action) {
|
||||
|
||||
case MenuItemInfo::UserDefined:
|
||||
sel = @selector(executeMenuItem:);
|
||||
|
||||
// TODO this is strange, it doesn't work, we receive the
|
||||
// message in OSXAppDelegate anyway. So
|
||||
// OSXAppDelegate.executeMenuItem: will redirect the message
|
||||
// to OSXNSMenuItem.executeMenuItem:
|
||||
target = m_handle;
|
||||
break;
|
||||
|
||||
case MenuItemInfo::Hide:
|
||||
sel = @selector(hide:);
|
||||
break;
|
||||
|
||||
case MenuItemInfo::HideOthers:
|
||||
sel = @selector(hideOtherApplications:);
|
||||
break;
|
||||
|
||||
case MenuItemInfo::ShowAll:
|
||||
sel = @selector(unhideAllApplications:);
|
||||
break;
|
||||
|
||||
case MenuItemInfo::Quit:
|
||||
sel = @selector(terminate:);
|
||||
break;
|
||||
|
||||
case MenuItemInfo::Minimize:
|
||||
sel = @selector(performMiniaturize:);
|
||||
break;
|
||||
|
||||
case MenuItemInfo::Zoom:
|
||||
sel = @selector(performZoom:);
|
||||
break;
|
||||
}
|
||||
|
||||
m_handle =
|
||||
[[OSXNSMenuItem alloc:this]
|
||||
initWithTitle:[NSString stringWithUTF8String:info.text.c_str()]
|
||||
action:sel
|
||||
keyEquivalent:@""];
|
||||
|
||||
m_handle.target = target;
|
||||
m_execute = info.execute;
|
||||
m_validate = info.validate;
|
||||
|
||||
if (!info.shortcut.isEmpty())
|
||||
setShortcut(info.shortcut);
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItemInfo::Separator:
|
||||
m_handle = [NSMenuItem separatorItem];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuItemOSX::dispose()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MenuItemOSX::setText(const std::string& text)
|
||||
{
|
||||
[m_handle setTitle:[NSString stringWithUTF8String:text.c_str()]];
|
||||
syncTitle();
|
||||
}
|
||||
|
||||
void MenuItemOSX::setSubmenu(Menu* submenu)
|
||||
{
|
||||
m_submenu = submenu;
|
||||
if (submenu) {
|
||||
[m_handle setSubmenu:((MenuOSX*)submenu)->handle()];
|
||||
syncTitle();
|
||||
}
|
||||
else
|
||||
[m_handle setSubmenu:nil];
|
||||
}
|
||||
|
||||
void MenuItemOSX::setEnabled(bool state)
|
||||
{
|
||||
m_handle.enabled = state;
|
||||
}
|
||||
|
||||
void MenuItemOSX::setChecked(bool state)
|
||||
{
|
||||
if (state)
|
||||
m_handle.state = NSOnState;
|
||||
else
|
||||
m_handle.state = NSOffState;
|
||||
}
|
||||
|
||||
void MenuItemOSX::setShortcut(const Shortcut& shortcut)
|
||||
{
|
||||
KeyModifiers mods = shortcut.modifiers();
|
||||
NSEventModifierFlags nsFlags = 0;
|
||||
if (mods & kKeyShiftModifier) nsFlags |= NSEventModifierFlagShift;
|
||||
if (mods & kKeyCtrlModifier) nsFlags |= NSEventModifierFlagControl;
|
||||
if (mods & kKeyAltModifier) nsFlags |= NSEventModifierFlagOption;
|
||||
if (mods & kKeyCmdModifier) nsFlags |= NSEventModifierFlagCommand;
|
||||
|
||||
NSString* keyStr;
|
||||
if (shortcut.unicode()) {
|
||||
unichar chr = shortcut.unicode();
|
||||
keyStr = [NSString stringWithCharacters:&chr length:1];
|
||||
}
|
||||
else
|
||||
keyStr = @"";
|
||||
|
||||
m_handle.keyEquivalent = keyStr;
|
||||
m_handle.keyEquivalentModifierMask = nsFlags;
|
||||
}
|
||||
|
||||
void MenuItemOSX::execute()
|
||||
{
|
||||
if (m_execute)
|
||||
m_execute();
|
||||
}
|
||||
|
||||
void MenuItemOSX::validate()
|
||||
{
|
||||
if (m_validate)
|
||||
m_validate(this);
|
||||
}
|
||||
|
||||
void MenuItemOSX::syncTitle()
|
||||
{
|
||||
// On macOS the submenu title is the one that is displayed in the
|
||||
// UI instead of the MenuItem title (so here we copy the menu item
|
||||
// title to the submenu title)
|
||||
if (m_submenu)
|
||||
[((MenuOSX*)m_submenu)->handle() setTitle:m_handle.title];
|
||||
}
|
||||
|
||||
MenuOSX::MenuOSX()
|
||||
{
|
||||
m_handle = [[OSXNSMenu alloc] init];
|
||||
}
|
||||
|
||||
void MenuOSX::dispose()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MenuOSX::addItem(MenuItem* item)
|
||||
{
|
||||
ASSERT(item);
|
||||
[m_handle addItem:((MenuItemOSX*)item)->handle()];
|
||||
}
|
||||
|
||||
void MenuOSX::insertItem(const int index, MenuItem* item)
|
||||
{
|
||||
ASSERT(item);
|
||||
[m_handle insertItem:((MenuItemOSX*)item)->handle()
|
||||
atIndex:index];
|
||||
}
|
||||
|
||||
MenusOSX::MenusOSX()
|
||||
{
|
||||
}
|
||||
|
||||
Menu* MenusOSX::createMenu()
|
||||
{
|
||||
return new MenuOSX;
|
||||
}
|
||||
|
||||
MenuItem* MenusOSX::createMenuItem(const MenuItemInfo& info)
|
||||
{
|
||||
return new MenuItemOSX(info);
|
||||
}
|
||||
|
||||
void MenusOSX::setAppMenu(Menu* menu)
|
||||
{
|
||||
if (menu)
|
||||
[NSApp setMainMenu:((MenuOSX*)menu)->handle()];
|
||||
else
|
||||
[NSApp setMainMenu:nil];
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,23 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_NATIVE_DIALOGS_H_INCLUDED
|
||||
#define OS_OSX_NATIVE_DIALOGS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/native_dialogs.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class NativeDialogsOSX : public NativeDialogs {
|
||||
public:
|
||||
NativeDialogsOSX();
|
||||
FileDialog* createFileDialog() override;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,174 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <vector>
|
||||
|
||||
#include "base/fs.h"
|
||||
#include "os/common/file_dialog.h"
|
||||
#include "os/display.h"
|
||||
#include "os/keys.h"
|
||||
#include "os/native_cursor.h"
|
||||
#include "os/osx/native_dialogs.h"
|
||||
|
||||
@interface OpenSaveHelper : NSObject {
|
||||
@private
|
||||
NSSavePanel* panel;
|
||||
os::Display* display;
|
||||
int result;
|
||||
}
|
||||
- (id)init;
|
||||
- (void)setPanel:(NSSavePanel*)panel;
|
||||
- (void)setDisplay:(os::Display*)display;
|
||||
- (void)runModal;
|
||||
- (int)result;
|
||||
@end
|
||||
|
||||
@implementation OpenSaveHelper
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
result = NSFileHandlingPanelCancelButton;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPanel:(NSSavePanel*)newPanel
|
||||
{
|
||||
panel = newPanel;
|
||||
}
|
||||
|
||||
- (void)setDisplay:(os::Display*)newDisplay
|
||||
{
|
||||
display = newDisplay;
|
||||
}
|
||||
|
||||
// This is executed in the main thread.
|
||||
- (void)runModal
|
||||
{
|
||||
os::NativeCursor oldCursor = display->nativeMouseCursor();
|
||||
display->setNativeMouseCursor(os::kArrowCursor);
|
||||
|
||||
#ifndef __MAC_10_6 // runModalForTypes is deprecated in 10.6
|
||||
if ([panel isKindOfClass:[NSOpenPanel class]]) {
|
||||
// As we're using OS X 10.4 framework, it looks like runModal
|
||||
// doesn't recognize the allowedFileTypes property. So we force it
|
||||
// using runModalForTypes: selector.
|
||||
|
||||
result = [(NSOpenPanel*)panel runModalForTypes:[panel allowedFileTypes]];
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
result = [panel runModal];
|
||||
}
|
||||
|
||||
display->setNativeMouseCursor(oldCursor);
|
||||
}
|
||||
|
||||
- (int)result
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace os {
|
||||
|
||||
class FileDialogOSX : public CommonFileDialog {
|
||||
public:
|
||||
FileDialogOSX() {
|
||||
}
|
||||
|
||||
std::string fileName() override {
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
void getMultipleFileNames(base::paths& output) override {
|
||||
output = m_filenames;
|
||||
}
|
||||
|
||||
void setFileName(const std::string& filename) override {
|
||||
m_filename = filename;
|
||||
}
|
||||
|
||||
bool show(Display* display) override {
|
||||
bool retValue = false;
|
||||
@autoreleasepool {
|
||||
NSSavePanel* panel = nil;
|
||||
|
||||
if (m_type == Type::SaveFile) {
|
||||
panel = [NSSavePanel new];
|
||||
}
|
||||
else {
|
||||
panel = [NSOpenPanel new];
|
||||
[(NSOpenPanel*)panel setAllowsMultipleSelection:(m_type == Type::OpenFiles ? YES: NO)];
|
||||
[(NSOpenPanel*)panel setCanChooseFiles:(m_type != Type::OpenFolder ? YES: NO)];
|
||||
[(NSOpenPanel*)panel setCanChooseDirectories:(m_type == Type::OpenFolder ? YES: NO)];
|
||||
}
|
||||
|
||||
[panel setTitle:[NSString stringWithUTF8String:m_title.c_str()]];
|
||||
[panel setCanCreateDirectories:YES];
|
||||
|
||||
std::string defPath = base::get_file_path(m_filename);
|
||||
std::string defName = base::get_file_name(m_filename);
|
||||
if (!defPath.empty())
|
||||
[panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:defPath.c_str()]]];
|
||||
if (!defName.empty())
|
||||
[panel setNameFieldStringValue:[NSString stringWithUTF8String:defName.c_str()]];
|
||||
|
||||
if (m_type != Type::OpenFolder && !m_filters.empty()) {
|
||||
NSMutableArray* types = [[NSMutableArray alloc] init];
|
||||
// The first extension in the array is used as the default one.
|
||||
if (!m_defExtension.empty())
|
||||
[types addObject:[NSString stringWithUTF8String:m_defExtension.c_str()]];
|
||||
for (const auto& filter : m_filters)
|
||||
[types addObject:[NSString stringWithUTF8String:filter.first.c_str()]];
|
||||
[panel setAllowedFileTypes:types];
|
||||
if (m_type == Type::SaveFile)
|
||||
[panel setAllowsOtherFileTypes:NO];
|
||||
}
|
||||
|
||||
OpenSaveHelper* helper = [OpenSaveHelper new];
|
||||
[helper setPanel:panel];
|
||||
[helper setDisplay:display];
|
||||
[helper performSelectorOnMainThread:@selector(runModal) withObject:nil waitUntilDone:YES];
|
||||
|
||||
if ([helper result] == NSFileHandlingPanelOKButton) {
|
||||
if (m_type == Type::OpenFiles) {
|
||||
for (NSURL* url in [(NSOpenPanel*)panel URLs]) {
|
||||
m_filename = [[url path] UTF8String];
|
||||
m_filenames.push_back(m_filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSURL* url = [panel URL];
|
||||
m_filename = [[url path] UTF8String];
|
||||
m_filenames.push_back(m_filename);
|
||||
}
|
||||
retValue = true;
|
||||
}
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string m_filename;
|
||||
base::paths m_filenames;
|
||||
};
|
||||
|
||||
NativeDialogsOSX::NativeDialogsOSX()
|
||||
{
|
||||
}
|
||||
|
||||
FileDialog* NativeDialogsOSX::createFileDialog()
|
||||
{
|
||||
return new FileDialogOSX();
|
||||
}
|
||||
|
||||
} // namespace os
|
@ -1,31 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_SYSTEM_H
|
||||
#define OS_OSX_SYSTEM_H
|
||||
#pragma once
|
||||
|
||||
#include "os/common/system.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
bool osx_is_key_pressed(KeyScancode scancode);
|
||||
int osx_get_unicode_from_scancode(KeyScancode scancode);
|
||||
|
||||
class OSXSystem : public CommonSystem {
|
||||
public:
|
||||
bool isKeyPressed(KeyScancode scancode) override {
|
||||
return osx_is_key_pressed(scancode);
|
||||
}
|
||||
|
||||
int getUnicodeFromScancode(KeyScancode scancode) override {
|
||||
return osx_get_unicode_from_scancode(scancode);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_VIEW_H_INCLUDED
|
||||
#define OS_OSX_VIEW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/pointer_type.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
@interface OSXView : NSView {
|
||||
@private
|
||||
NSTrackingArea* m_trackingArea;
|
||||
NSCursor* m_nsCursor;
|
||||
bool m_visibleMouse;
|
||||
os::PointerType m_pointerType;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)frameRect;
|
||||
- (BOOL)acceptsFirstResponder;
|
||||
- (void)viewDidHide;
|
||||
- (void)viewDidUnhide;
|
||||
- (void)viewDidMoveToWindow;
|
||||
- (void)drawRect:(NSRect)dirtyRect;
|
||||
- (void)keyDown:(NSEvent*)event;
|
||||
- (void)keyUp:(NSEvent*)event;
|
||||
- (void)flagsChanged:(NSEvent*)event;
|
||||
+ (void)updateKeyFlags:(NSEvent*)event;
|
||||
- (void)mouseEntered:(NSEvent*)event;
|
||||
- (void)mouseMoved:(NSEvent*)event;
|
||||
- (void)mouseExited:(NSEvent*)event;
|
||||
- (void)mouseDown:(NSEvent*)event;
|
||||
- (void)mouseUp:(NSEvent*)event;
|
||||
- (void)mouseDragged:(NSEvent*)event;
|
||||
- (void)rightMouseDown:(NSEvent*)event;
|
||||
- (void)rightMouseUp:(NSEvent*)event;
|
||||
- (void)rightMouseDragged:(NSEvent*)event;
|
||||
- (void)otherMouseDown:(NSEvent*)event;
|
||||
- (void)otherMouseUp:(NSEvent*)event;
|
||||
- (void)otherMouseDragged:(NSEvent*)event;
|
||||
- (void)handleMouseDown:(NSEvent*)event;
|
||||
- (void)handleMouseUp:(NSEvent*)event;
|
||||
- (void)handleMouseDragged:(NSEvent*)event;
|
||||
- (void)scrollWheel:(NSEvent*)event;
|
||||
- (void)cursorUpdate:(NSEvent*)event;
|
||||
- (void)setCursor:(NSCursor*)cursor;
|
||||
- (void)setFrameSize:(NSSize)newSize;
|
||||
- (void)createMouseTrackingArea;
|
||||
- (void)destroyMouseTrackingArea;
|
||||
- (void)updateCurrentCursor;
|
||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
|
||||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
|
||||
- (void)doCommandBySelector:(SEL)selector;
|
||||
- (void)setTranslateDeadKeys:(BOOL)state;
|
||||
@end
|
||||
|
||||
#endif
|
@ -1,585 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#define KEY_TRACE(...)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/osx/view.h"
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "os/event.h"
|
||||
#include "os/event_queue.h"
|
||||
#include "os/osx/generate_drop_files.h"
|
||||
#include "os/osx/keys.h"
|
||||
#include "os/osx/window.h"
|
||||
#include "os/system.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
// Global variable used between View and OSXNSMenu to check if the
|
||||
// keyDown: event was used by a key equivalent in the menu.
|
||||
//
|
||||
// TODO I'm not proud of this, but it does the job
|
||||
bool g_keyEquivalentUsed = false;
|
||||
|
||||
bool osx_is_key_pressed(KeyScancode scancode);
|
||||
|
||||
namespace {
|
||||
|
||||
// Internal array of pressed keys used in isKeyPressed()
|
||||
int g_pressedKeys[kKeyScancodes];
|
||||
bool g_translateDeadKeys = false;
|
||||
UInt32 g_lastDeadKeyState = 0;
|
||||
|
||||
gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
{
|
||||
NSPoint point = [view convertPoint:[event locationInWindow]
|
||||
fromView:nil];
|
||||
int scale = 1;
|
||||
if ([view window])
|
||||
scale = [(OSXWindow*)[view window] scale];
|
||||
|
||||
// "os" layer coordinates expect (X,Y) origin at the top-left corner.
|
||||
return gfx::Point(point.x / scale,
|
||||
(view.bounds.size.height - point.y) / scale);
|
||||
}
|
||||
|
||||
Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
{
|
||||
// Some Wacom drivers on OS X report right-clicks with
|
||||
// buttonNumber=0, so we've to check the type event anyway.
|
||||
switch (event.type) {
|
||||
case NSEventTypeLeftMouseDown:
|
||||
case NSEventTypeLeftMouseUp:
|
||||
case NSEventTypeLeftMouseDragged:
|
||||
return Event::LeftButton;
|
||||
case NSEventTypeRightMouseDown:
|
||||
case NSEventTypeRightMouseUp:
|
||||
case NSEventTypeRightMouseDragged:
|
||||
return Event::RightButton;
|
||||
}
|
||||
|
||||
switch (event.buttonNumber) {
|
||||
case 0: return Event::LeftButton; break;
|
||||
case 1: return Event::RightButton; break;
|
||||
case 2: return Event::MiddleButton; break;
|
||||
// NSOtherMouseDown/Up/Dragged
|
||||
case 3: return Event::X1Button; break;
|
||||
case 4: return Event::X2Button; break;
|
||||
}
|
||||
|
||||
return Event::MouseButton::NoneButton;
|
||||
}
|
||||
|
||||
KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
{
|
||||
int modifiers = kKeyNoneModifier;
|
||||
NSEventModifierFlags nsFlags = event.modifierFlags;
|
||||
if (nsFlags & NSEventModifierFlagShift) modifiers |= kKeyShiftModifier;
|
||||
if (nsFlags & NSEventModifierFlagControl) modifiers |= kKeyCtrlModifier;
|
||||
if (nsFlags & NSEventModifierFlagOption) modifiers |= kKeyAltModifier;
|
||||
if (nsFlags & NSEventModifierFlagCommand) modifiers |= kKeyCmdModifier;
|
||||
if (osx_is_key_pressed(kKeySpace)) modifiers |= kKeySpaceModifier;
|
||||
return (KeyModifiers)modifiers;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool osx_is_key_pressed(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return (g_pressedKeys[scancode] != 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int osx_get_unicode_from_scancode(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return g_pressedKeys[scancode];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
|
||||
using namespace os;
|
||||
|
||||
@implementation OSXView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frameRect
|
||||
{
|
||||
// We start without the system mouse cursor
|
||||
m_nsCursor = nil;
|
||||
m_visibleMouse = true;
|
||||
m_pointerType = os::PointerType::Unknown;
|
||||
|
||||
self = [super initWithFrame:frameRect];
|
||||
if (self != nil) {
|
||||
[self createMouseTrackingArea];
|
||||
[self registerForDraggedTypes:
|
||||
[NSArray arrayWithObjects:
|
||||
NSFilenamesPboardType,
|
||||
nil]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)viewDidHide
|
||||
{
|
||||
[super viewDidHide];
|
||||
[self destroyMouseTrackingArea];
|
||||
}
|
||||
|
||||
- (void)viewDidUnhide
|
||||
{
|
||||
[super viewDidUnhide];
|
||||
[self createMouseTrackingArea];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[super viewDidMoveToWindow];
|
||||
|
||||
if ([self window]) {
|
||||
OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl];
|
||||
if (impl)
|
||||
impl->onWindowChanged();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
[super drawRect:dirtyRect];
|
||||
|
||||
if ([self window]) {
|
||||
OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl];
|
||||
if (impl)
|
||||
impl->onDrawRect(gfx::Rect(dirtyRect.origin.x,
|
||||
dirtyRect.origin.y,
|
||||
dirtyRect.size.width,
|
||||
dirtyRect.size.height));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent*)event
|
||||
{
|
||||
g_keyEquivalentUsed = false;
|
||||
[super keyDown:event];
|
||||
|
||||
// If a key equivalent used the keyDown event, we don't generate
|
||||
// this os::KeyDown event.
|
||||
if (g_keyEquivalentUsed)
|
||||
return;
|
||||
|
||||
KeyScancode scancode = scancode_from_nsevent(event);
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
bool sendMsg = true;
|
||||
|
||||
CFStringRef strRef = get_unicode_from_key_code(event.keyCode,
|
||||
event.modifierFlags);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length == 1)
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, 0));
|
||||
CFRelease(strRef);
|
||||
}
|
||||
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
g_pressedKeys[scancode] = (ev.unicodeChar() ? ev.unicodeChar(): 1);
|
||||
|
||||
if (g_translateDeadKeys) {
|
||||
strRef = get_unicode_from_key_code(event.keyCode,
|
||||
event.modifierFlags,
|
||||
&g_lastDeadKeyState);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length > 0) {
|
||||
sendMsg = false;
|
||||
for (int i=0; i<length; ++i) {
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, i));
|
||||
queue_event(ev);
|
||||
}
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
else {
|
||||
ev.setDeadKey(true);
|
||||
}
|
||||
CFRelease(strRef);
|
||||
}
|
||||
}
|
||||
|
||||
KEY_TRACE("View keyDown: unicode=%d (%c) scancode=%d modifiers=%d\n",
|
||||
ev.unicodeChar(), ev.unicodeChar(),
|
||||
ev.scancode(), ev.modifiers());
|
||||
|
||||
if (sendMsg)
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent*)event
|
||||
{
|
||||
[super keyUp:event];
|
||||
|
||||
KeyScancode scancode = scancode_from_nsevent(event);
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
g_pressedKeys[scancode] = 0;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyUp);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)flagsChanged:(NSEvent*)event
|
||||
{
|
||||
[super flagsChanged:event];
|
||||
[OSXView updateKeyFlags:event];
|
||||
}
|
||||
|
||||
+ (void)updateKeyFlags:(NSEvent*)event
|
||||
{
|
||||
static int lastFlags = 0;
|
||||
static int flags[] = {
|
||||
NSEventModifierFlagShift,
|
||||
NSEventModifierFlagControl,
|
||||
NSEventModifierFlagOption,
|
||||
NSEventModifierFlagCommand
|
||||
};
|
||||
static KeyScancode scancodes[] = {
|
||||
kKeyLShift,
|
||||
kKeyLControl,
|
||||
kKeyAlt,
|
||||
kKeyCommand
|
||||
};
|
||||
|
||||
KeyModifiers modifiers = get_modifiers_from_nsevent(event);
|
||||
int newFlags = event.modifierFlags;
|
||||
|
||||
for (int i=0; i<sizeof(flags)/sizeof(flags[0]); ++i) {
|
||||
if ((lastFlags & flags[i]) != (newFlags & flags[i])) {
|
||||
Event ev;
|
||||
ev.setType(
|
||||
((newFlags & flags[i]) != 0 ? Event::KeyDown:
|
||||
Event::KeyUp));
|
||||
|
||||
g_pressedKeys[scancodes[i]] = ((newFlags & flags[i]) != 0);
|
||||
|
||||
ev.setScancode(scancodes[i]);
|
||||
ev.setModifiers(modifiers);
|
||||
ev.setRepeat(0);
|
||||
queue_event(ev);
|
||||
}
|
||||
}
|
||||
|
||||
lastFlags = newFlags;
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
[self updateCurrentCursor];
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::MouseEnter);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(Event::MouseMove);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
|
||||
if (m_pointerType != os::PointerType::Unknown)
|
||||
ev.setPointerType(m_pointerType);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
// Restore arrow cursor
|
||||
if (!m_visibleMouse) {
|
||||
m_visibleMouse = true;
|
||||
[NSCursor unhide];
|
||||
}
|
||||
[[NSCursor arrowCursor] set];
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::MouseLeave);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDown:event];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseUp:event];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDragged:event];
|
||||
}
|
||||
|
||||
- (void)rightMouseDown:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDown:event];
|
||||
}
|
||||
|
||||
- (void)rightMouseUp:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseUp:event];
|
||||
}
|
||||
|
||||
- (void)rightMouseDragged:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDragged:event];
|
||||
}
|
||||
|
||||
- (void)otherMouseDown:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDown:event];
|
||||
}
|
||||
|
||||
- (void)otherMouseUp:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseUp:event];
|
||||
}
|
||||
|
||||
- (void)otherMouseDragged:(NSEvent*)event
|
||||
{
|
||||
[self handleMouseDragged:event];
|
||||
}
|
||||
|
||||
- (void)handleMouseDown:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(event.clickCount == 2 ? Event::MouseDoubleClick:
|
||||
Event::MouseDown);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setButton(get_mouse_buttons(event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
|
||||
if (m_pointerType != os::PointerType::Unknown)
|
||||
ev.setPointerType(m_pointerType);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)handleMouseUp:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(Event::MouseUp);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setButton(get_mouse_buttons(event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
|
||||
if (m_pointerType != os::PointerType::Unknown)
|
||||
ev.setPointerType(m_pointerType);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)handleMouseDragged:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(Event::MouseMove);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setButton(get_mouse_buttons(event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
|
||||
if (m_pointerType != os::PointerType::Unknown)
|
||||
ev.setPointerType(m_pointerType);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(NSSize)newSize
|
||||
{
|
||||
[super setFrameSize:newSize];
|
||||
|
||||
// Re-create the mouse tracking area
|
||||
[self destroyMouseTrackingArea];
|
||||
[self createMouseTrackingArea];
|
||||
|
||||
// Call OSXWindowImpl::onResize handler
|
||||
if ([self window]) {
|
||||
OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl];
|
||||
if (impl)
|
||||
impl->onResize(gfx::Size(newSize.width,
|
||||
newSize.height));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(Event::MouseWheel);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setButton(get_mouse_buttons(event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
|
||||
int scale = 1;
|
||||
if (self.window)
|
||||
scale = [(OSXWindow*)self.window scale];
|
||||
|
||||
if (event.hasPreciseScrollingDeltas) {
|
||||
ev.setPointerType(os::PointerType::Touchpad);
|
||||
ev.setWheelDelta(gfx::Point(-event.scrollingDeltaX / scale,
|
||||
-event.scrollingDeltaY / scale));
|
||||
ev.setPreciseWheel(true);
|
||||
}
|
||||
else {
|
||||
// Ignore the acceleration factor, just use the wheel sign.
|
||||
gfx::Point pt(0, 0);
|
||||
if (event.scrollingDeltaX >= 0.1)
|
||||
pt.x = -1;
|
||||
else if (event.scrollingDeltaX <= -0.1)
|
||||
pt.x = 1;
|
||||
if (event.scrollingDeltaY >= 0.1)
|
||||
pt.y = -1;
|
||||
else if (event.scrollingDeltaY <= -0.1)
|
||||
pt.y = 1;
|
||||
|
||||
ev.setPointerType(os::PointerType::Mouse);
|
||||
ev.setWheelDelta(pt);
|
||||
}
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)magnifyWithEvent:(NSEvent*)event
|
||||
{
|
||||
Event ev;
|
||||
ev.setType(Event::TouchMagnify);
|
||||
ev.setMagnification(event.magnification);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setPointerType(os::PointerType::Touchpad);
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)tabletProximity:(NSEvent*)event
|
||||
{
|
||||
if (event.isEnteringProximity == YES) {
|
||||
switch (event.pointingDeviceType) {
|
||||
case NSPointingDeviceTypePen: m_pointerType = os::PointerType::Pen; break;
|
||||
case NSPointingDeviceTypeCursor: m_pointerType = os::PointerType::Cursor; break;
|
||||
case NSPointingDeviceTypeEraser: m_pointerType = os::PointerType::Eraser; break;
|
||||
default:
|
||||
m_pointerType = os::PointerType::Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_pointerType = os::PointerType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(NSEvent*)event
|
||||
{
|
||||
[self updateCurrentCursor];
|
||||
}
|
||||
|
||||
- (void)setCursor:(NSCursor*)cursor
|
||||
{
|
||||
m_nsCursor = cursor;
|
||||
[self updateCurrentCursor];
|
||||
}
|
||||
|
||||
- (void)createMouseTrackingArea
|
||||
{
|
||||
// Create a tracking area to receive mouseMoved events
|
||||
m_trackingArea =
|
||||
[[NSTrackingArea alloc]
|
||||
initWithRect:self.bounds
|
||||
options:(NSTrackingMouseEnteredAndExited |
|
||||
NSTrackingMouseMoved |
|
||||
NSTrackingActiveAlways |
|
||||
NSTrackingEnabledDuringMouseDrag)
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:m_trackingArea];
|
||||
}
|
||||
|
||||
- (void)destroyMouseTrackingArea
|
||||
{
|
||||
[self removeTrackingArea:m_trackingArea];
|
||||
m_trackingArea = nil;
|
||||
}
|
||||
|
||||
- (void)updateCurrentCursor
|
||||
{
|
||||
if (m_nsCursor) {
|
||||
if (!m_visibleMouse) {
|
||||
m_visibleMouse = true;
|
||||
[NSCursor unhide];
|
||||
}
|
||||
[m_nsCursor set];
|
||||
}
|
||||
else if (m_visibleMouse) {
|
||||
m_visibleMouse = false;
|
||||
[NSCursor hide];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
return NSDragOperationCopy;
|
||||
}
|
||||
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
NSPasteboard* pasteboard = [sender draggingPasteboard];
|
||||
|
||||
if ([pasteboard.types containsObject:NSFilenamesPboardType]) {
|
||||
NSArray* filenames = [pasteboard propertyListForType:NSFilenamesPboardType];
|
||||
generate_drop_files_from_nsarray(filenames);
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)selector
|
||||
{
|
||||
// Do nothing (avoid beep pressing Escape key)
|
||||
}
|
||||
|
||||
- (void)setTranslateDeadKeys:(BOOL)state
|
||||
{
|
||||
g_translateDeadKeys = (state ? true: false);
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
|
||||
@end
|
@ -1,56 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_OSX_WINDOW_H_INCLUDED
|
||||
#define OS_OSX_WINDOW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/size.h"
|
||||
#include "os/keys.h"
|
||||
#include "os/native_cursor.h"
|
||||
|
||||
namespace os {
|
||||
class Surface;
|
||||
}
|
||||
|
||||
class OSXWindowImpl {
|
||||
public:
|
||||
virtual ~OSXWindowImpl() { }
|
||||
virtual void onClose() = 0;
|
||||
virtual void onResize(const gfx::Size& size) = 0;
|
||||
virtual void onDrawRect(const gfx::Rect& rect) = 0;
|
||||
virtual void onWindowChanged() = 0;
|
||||
};
|
||||
|
||||
@class OSXWindowDelegate;
|
||||
|
||||
@interface OSXWindow : NSWindow {
|
||||
@private
|
||||
OSXWindowImpl* m_impl;
|
||||
OSXWindowDelegate* m_delegate;
|
||||
int m_scale;
|
||||
}
|
||||
- (OSXWindow*)initWithImpl:(OSXWindowImpl*)impl
|
||||
width:(int)width
|
||||
height:(int)height
|
||||
scale:(int)scale;
|
||||
- (OSXWindowImpl*)impl;
|
||||
- (int)scale;
|
||||
- (void)setScale:(int)scale;
|
||||
- (gfx::Size)clientSize;
|
||||
- (gfx::Size)restoredSize;
|
||||
- (void)setMousePosition:(const gfx::Point&)position;
|
||||
- (BOOL)setNativeMouseCursor:(os::NativeCursor)cursor;
|
||||
- (BOOL)setNativeMouseCursor:(const os::Surface*)surface
|
||||
focus:(const gfx::Point&)focus
|
||||
scale:(const int)scale;
|
||||
@end
|
||||
|
||||
#endif
|
@ -1,242 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "os/osx/window.h"
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "os/event.h"
|
||||
#include "os/osx/event_queue.h"
|
||||
#include "os/osx/view.h"
|
||||
#include "os/osx/window_delegate.h"
|
||||
#include "os/surface.h"
|
||||
|
||||
using namespace os;
|
||||
|
||||
@implementation OSXWindow
|
||||
|
||||
- (OSXWindow*)initWithImpl:(OSXWindowImpl*)impl
|
||||
width:(int)width
|
||||
height:(int)height
|
||||
scale:(int)scale
|
||||
{
|
||||
m_impl = impl;
|
||||
m_scale = scale;
|
||||
|
||||
NSRect rect = NSMakeRect(0, 0, width, height);
|
||||
self = [self initWithContentRect:rect
|
||||
styleMask:(NSWindowStyleMaskTitled |
|
||||
NSWindowStyleMaskClosable |
|
||||
NSWindowStyleMaskMiniaturizable |
|
||||
NSWindowStyleMaskResizable)
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
if (!self)
|
||||
return nil;
|
||||
|
||||
m_delegate = [[OSXWindowDelegate alloc] initWithWindowImpl:impl];
|
||||
|
||||
// The NSView width and height will be a multiple of 4. In this way
|
||||
// all scaled pixels should be exactly the same
|
||||
// for Screen Scaling > 1 and <= 4)
|
||||
self.contentResizeIncrements = NSMakeSize(4, 4);
|
||||
|
||||
OSXView* view = [[OSXView alloc] initWithFrame:rect];
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
[self setDelegate:m_delegate];
|
||||
[self setContentView:view];
|
||||
[self center];
|
||||
[self makeKeyAndOrderFront:self];
|
||||
|
||||
// Hide the "View > Show Tab Bar" menu item
|
||||
if ([self respondsToSelector:@selector(setTabbingMode:)])
|
||||
[self setTabbingMode:NSWindowTabbingModeDisallowed];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (OSXWindowImpl*)impl
|
||||
{
|
||||
return m_impl;
|
||||
}
|
||||
|
||||
- (int)scale
|
||||
{
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
- (void)setScale:(int)scale
|
||||
{
|
||||
m_scale = scale;
|
||||
|
||||
if (m_impl) {
|
||||
NSRect bounds = [[self contentView] bounds];
|
||||
m_impl->onResize(gfx::Size(bounds.size.width,
|
||||
bounds.size.height));
|
||||
}
|
||||
}
|
||||
|
||||
- (gfx::Size)clientSize
|
||||
{
|
||||
return gfx::Size([[self contentView] frame].size.width,
|
||||
[[self contentView] frame].size.height);
|
||||
}
|
||||
|
||||
- (gfx::Size)restoredSize
|
||||
{
|
||||
return [self clientSize];
|
||||
}
|
||||
|
||||
- (void)setMousePosition:(const gfx::Point&)position
|
||||
{
|
||||
NSView* view = self.contentView;
|
||||
NSPoint pt = NSMakePoint(
|
||||
position.x*m_scale,
|
||||
view.frame.size.height - position.y*m_scale);
|
||||
|
||||
pt = [view convertPoint:pt toView:view];
|
||||
pt = [view convertPoint:pt toView:nil];
|
||||
pt = [self convertBaseToScreen:pt];
|
||||
pt.y = [[self screen] frame].size.height - pt.y;
|
||||
|
||||
CGPoint pos = CGPointMake(pt.x, pt.y);
|
||||
CGEventRef event = CGEventCreateMouseEvent(
|
||||
NULL, kCGEventMouseMoved, pos, kCGMouseButtonLeft);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
}
|
||||
|
||||
- (BOOL)setNativeMouseCursor:(NativeCursor)cursor
|
||||
{
|
||||
NSCursor* nsCursor = nullptr;
|
||||
|
||||
switch (cursor) {
|
||||
case kArrowCursor:
|
||||
case kWaitCursor:
|
||||
case kHelpCursor:
|
||||
case kSizeNECursor:
|
||||
case kSizeNWCursor:
|
||||
case kSizeSECursor:
|
||||
case kSizeSWCursor:
|
||||
nsCursor = [NSCursor arrowCursor];
|
||||
break;
|
||||
case kCrosshairCursor:
|
||||
nsCursor = [NSCursor crosshairCursor];
|
||||
break;
|
||||
case kIBeamCursor:
|
||||
nsCursor = [NSCursor IBeamCursor];
|
||||
break;
|
||||
case kLinkCursor:
|
||||
nsCursor = [NSCursor pointingHandCursor];
|
||||
break;
|
||||
case kForbiddenCursor:
|
||||
nsCursor = [NSCursor operationNotAllowedCursor];
|
||||
break;
|
||||
case kMoveCursor:
|
||||
nsCursor = [NSCursor openHandCursor];
|
||||
break;
|
||||
case kSizeNSCursor:
|
||||
nsCursor = [NSCursor resizeUpDownCursor];
|
||||
break;
|
||||
case kSizeWECursor:
|
||||
nsCursor = [NSCursor resizeLeftRightCursor];
|
||||
break;
|
||||
case kSizeNCursor:
|
||||
nsCursor = [NSCursor resizeUpCursor];
|
||||
break;
|
||||
case kSizeECursor:
|
||||
nsCursor = [NSCursor resizeRightCursor];
|
||||
break;
|
||||
case kSizeSCursor:
|
||||
nsCursor = [NSCursor resizeDownCursor];
|
||||
break;
|
||||
case kSizeWCursor:
|
||||
nsCursor = [NSCursor resizeLeftCursor];
|
||||
break;
|
||||
}
|
||||
|
||||
[self.contentView setCursor:nsCursor];
|
||||
return (nsCursor ? YES: NO);
|
||||
}
|
||||
|
||||
- (BOOL)setNativeMouseCursor:(const os::Surface*)surface
|
||||
focus:(const gfx::Point&)focus
|
||||
scale:(const int)scale
|
||||
{
|
||||
ASSERT(surface);
|
||||
SurfaceFormatData format;
|
||||
surface->getFormat(&format);
|
||||
if (format.bitsPerPixel != 32)
|
||||
return NO;
|
||||
|
||||
const int w = scale*surface->width();
|
||||
const int h = scale*surface->height();
|
||||
|
||||
if (4*w*h == 0)
|
||||
return NO;
|
||||
|
||||
@autoreleasepool {
|
||||
NSBitmapImageRep* bmp =
|
||||
[[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes:nil
|
||||
pixelsWide:w
|
||||
pixelsHigh:h
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
|
||||
bytesPerRow:w*4
|
||||
bitsPerPixel:32];
|
||||
if (!bmp)
|
||||
return NO;
|
||||
|
||||
uint32_t* dst = (uint32_t*)[bmp bitmapData];
|
||||
for (int y=0; y<h; ++y) {
|
||||
const uint32_t* src = (const uint32_t*)surface->getData(0, y/scale);
|
||||
for (int x=0, u=0; x<w; ++x, ++dst) {
|
||||
*dst = *src;
|
||||
if (++u == scale) {
|
||||
u = 0;
|
||||
++src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSImage* img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
|
||||
if (!img)
|
||||
return NO;
|
||||
|
||||
[img addRepresentation:bmp];
|
||||
|
||||
NSCursor* nsCursor =
|
||||
[[NSCursor alloc] initWithImage:img
|
||||
hotSpot:NSMakePoint(scale*focus.x + scale/2,
|
||||
scale*focus.y + scale/2)];
|
||||
if (!nsCursor)
|
||||
return NO;
|
||||
|
||||
[self.contentView setCursor:nsCursor];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noResponderFor:(SEL)eventSelector
|
||||
{
|
||||
if (eventSelector == @selector(keyDown:)) {
|
||||
// Do nothing (avoid beep)
|
||||
}
|
||||
else {
|
||||
[super noResponderFor:eventSelector];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -1,45 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
class OSXWindowImpl;
|
||||
|
||||
@interface OSXWindowDelegate : NSObject {
|
||||
@private
|
||||
OSXWindowImpl* m_impl;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation OSXWindowDelegate
|
||||
|
||||
- (OSXWindowDelegate*)initWithWindowImpl:(OSXWindowImpl*)impl
|
||||
{
|
||||
m_impl = impl;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender
|
||||
{
|
||||
os::Event ev;
|
||||
ev.setType(os::Event::CloseDisplay);
|
||||
//ev.setDisplay(nullptr); // TODO
|
||||
os::queue_event(ev);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)notification
|
||||
{
|
||||
m_impl->onClose();
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification*)notification
|
||||
{
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)notification
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
@ -1,26 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_POINTER_TYPE_H_INCLUDED
|
||||
#define OS_POINTER_TYPE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
// Source of a mouse like event
|
||||
enum class PointerType {
|
||||
Unknown,
|
||||
Mouse, // A regular mouse
|
||||
Touchpad, // Touchpad/trackpad
|
||||
Touch, // Touch screen
|
||||
Pen, // Stylus pen
|
||||
Cursor, // Puck like device
|
||||
Eraser // Eraser end of a stylus pen
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2012-2013 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_SCOPED_HANDLE_H_INCLUDED
|
||||
#define OS_SCOPED_HANDLE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace os {
|
||||
|
||||
template<typename T>
|
||||
class ScopedHandle {
|
||||
public:
|
||||
ScopedHandle(T* handle) : m_handle(handle) { }
|
||||
~ScopedHandle() {
|
||||
if (m_handle)
|
||||
m_handle->dispose();
|
||||
}
|
||||
|
||||
T* operator->() { return m_handle; }
|
||||
operator T*() { return m_handle; }
|
||||
private:
|
||||
T* m_handle;
|
||||
|
||||
// Cannot copy
|
||||
ScopedHandle(const ScopedHandle&);
|
||||
ScopedHandle& operator=(const ScopedHandle&);
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
// LAF OS Library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef OS_SHORTCUT_H_INCLUDED
|
||||
#define OS_SHORTCUT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/keys.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
class Shortcut {
|
||||
public:
|
||||
Shortcut(int unicode = 0,
|
||||
KeyModifiers modifiers = kKeyNoneModifier)
|
||||
: m_unicode(unicode)
|
||||
, m_modifiers(modifiers) {
|
||||
}
|
||||
|
||||
int unicode() const { return m_unicode; }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
|
||||
bool isEmpty() const { return m_unicode == 0; }
|
||||
|
||||
private:
|
||||
int m_unicode;
|
||||
KeyModifiers m_modifiers;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user