gosu 1.4.6 → 2.0.0.pre6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/COPYING +2 -1
- data/dependencies/SDL/include/SDL_atomic.h +2 -3
- data/dependencies/SDL/include/SDL_audio.h +7 -7
- data/dependencies/SDL/include/SDL_blendmode.h +1 -1
- data/dependencies/SDL/include/SDL_endian.h +3 -3
- data/dependencies/SDL/include/SDL_gamecontroller.h +4 -4
- data/dependencies/SDL/include/SDL_hints.h +72 -28
- data/dependencies/SDL/include/SDL_joystick.h +8 -5
- data/dependencies/SDL/include/SDL_keycode.h +1 -1
- data/dependencies/SDL/include/SDL_main.h +7 -0
- data/dependencies/SDL/include/SDL_mouse.h +6 -7
- data/dependencies/SDL/include/SDL_mutex.h +79 -5
- data/dependencies/SDL/include/SDL_opengl_glext.h +5 -1
- data/dependencies/SDL/include/SDL_power.h +7 -8
- data/dependencies/SDL/include/SDL_render.h +5 -0
- data/dependencies/SDL/include/SDL_revision.h +2 -2
- data/dependencies/SDL/include/SDL_sensor.h +1 -1
- data/dependencies/SDL/include/SDL_stdinc.h +19 -11
- data/dependencies/SDL/include/SDL_thread.h +2 -2
- data/dependencies/SDL/include/SDL_version.h +2 -2
- data/dependencies/SDL/include/SDL_video.h +30 -2
- data/dependencies/SDL/include/begin_code.h +7 -7
- data/dependencies/SDL/include/close_code.h +2 -2
- data/dependencies/SDL/lib/x64/libSDL2.dll.a +0 -0
- data/dependencies/SDL/lib/x86/libSDL2.dll.a +0 -0
- data/dependencies/SDL_sound/SDL_sound.h +1 -1
- data/dependencies/SDL_sound/dr_flac.h +48 -23
- data/dependencies/SDL_sound/dr_mp3.h +34 -14
- data/dependencies/SDL_sound/stb_vorbis.h +3 -2
- data/dependencies/mojoAL/mojoal.c +1 -1
- data/ext/{gosu → gosu-ffi}/extconf.rb +34 -33
- data/ext/gosu-ffi/gosu-ffi.def +464 -0
- data/ffi/Gosu.cpp +307 -0
- data/ffi/Gosu.h +84 -0
- data/ffi/Gosu_Channel.cpp +62 -0
- data/ffi/Gosu_Channel.h +17 -0
- data/ffi/Gosu_Color.cpp +132 -0
- data/ffi/Gosu_Color.h +31 -0
- data/ffi/Gosu_Constants.cpp +334 -0
- data/ffi/Gosu_FFI.h +34 -0
- data/ffi/Gosu_FFI_internal.h +161 -0
- data/ffi/Gosu_Font.cpp +92 -0
- data/ffi/Gosu_Font.h +32 -0
- data/ffi/Gosu_Image.cpp +206 -0
- data/ffi/Gosu_Image.h +60 -0
- data/ffi/Gosu_Sample.cpp +29 -0
- data/ffi/Gosu_Sample.h +14 -0
- data/ffi/Gosu_Song.cpp +69 -0
- data/ffi/Gosu_Song.h +18 -0
- data/ffi/Gosu_TextInput.cpp +94 -0
- data/ffi/Gosu_TextInput.h +25 -0
- data/ffi/Gosu_Window.cpp +314 -0
- data/ffi/Gosu_Window.h +78 -0
- data/include/Gosu/Audio.hpp +6 -11
- data/include/Gosu/Bitmap.hpp +38 -53
- data/include/Gosu/Buffer.hpp +54 -0
- data/include/Gosu/Color.hpp +27 -35
- data/include/Gosu/Directories.hpp +25 -28
- data/include/Gosu/Drawable.hpp +58 -0
- data/include/Gosu/Font.hpp +6 -5
- data/include/Gosu/Fwd.hpp +4 -6
- data/include/Gosu/Gosu.hpp +5 -5
- data/include/Gosu/Graphics.hpp +51 -61
- data/include/Gosu/GraphicsBase.hpp +1 -11
- data/include/Gosu/Image.hpp +11 -14
- data/include/Gosu/Math.hpp +50 -72
- data/include/Gosu/Transform.hpp +32 -0
- data/include/Gosu/Utility.hpp +51 -1
- data/include/Gosu/Version.hpp +3 -3
- data/include/Gosu/Window.hpp +15 -9
- data/lib/SDL2.dll +0 -0
- data/lib/gosu/channel.rb +49 -0
- data/lib/gosu/color.rb +150 -0
- data/lib/gosu/compat.rb +29 -8
- data/lib/gosu/constants.rb +386 -0
- data/lib/gosu/ffi.rb +258 -0
- data/lib/gosu/font.rb +56 -0
- data/lib/gosu/gl_tex_info.rb +33 -0
- data/lib/gosu/gosu.rb +210 -0
- data/lib/gosu/image.rb +141 -0
- data/lib/gosu/numeric.rb +17 -0
- data/lib/gosu/preview.rb +6 -6
- data/lib/gosu/sample.rb +24 -0
- data/lib/gosu/song.rb +56 -0
- data/lib/gosu/text_input.rb +69 -0
- data/lib/gosu/window.rb +228 -0
- data/lib/gosu.rb +29 -8
- data/lib64/SDL2.dll +0 -0
- data/rdoc/gosu.rb +0 -2
- data/src/Audio.cpp +12 -12
- data/src/AudioFile.hpp +5 -4
- data/src/AudioFileAudioToolbox.cpp +8 -8
- data/src/AudioFileSDLSound.cpp +7 -10
- data/src/BinPacker.cpp +187 -0
- data/src/BinPacker.hpp +55 -0
- data/src/Bitmap.cpp +166 -144
- data/src/BitmapIO.cpp +60 -86
- data/src/Buffer.cpp +159 -0
- data/src/Color.cpp +75 -80
- data/src/Directories.cpp +47 -0
- data/src/DirectoriesUIKit.cpp +50 -0
- data/src/DrawOp.hpp +9 -4
- data/src/DrawOpQueue.hpp +2 -2
- data/src/Drawable.cpp +95 -0
- data/src/EmptyDrawable.hpp +38 -0
- data/src/FPS.cpp +31 -0
- data/src/Font.cpp +104 -74
- data/src/GosuGLView.cpp +14 -6
- data/src/GosuViewController.cpp +2 -10
- data/src/Graphics.cpp +60 -126
- data/src/GraphicsImpl.hpp +17 -47
- data/src/Image.cpp +41 -35
- data/src/Input.cpp +7 -8
- data/src/Macro.cpp +6 -6
- data/src/Macro.hpp +4 -4
- data/src/MarkupParser.cpp +5 -5
- data/src/Math.cpp +35 -22
- data/src/OffScreenTarget.cpp +53 -49
- data/src/OffScreenTarget.hpp +13 -11
- data/src/OpenGLContext.cpp +117 -0
- data/src/OpenGLContext.hpp +41 -0
- data/src/RenderState.hpp +21 -19
- data/src/Resolution.cpp +23 -21
- data/src/TexChunk.cpp +35 -80
- data/src/TexChunk.hpp +44 -35
- data/src/Text.cpp +1 -1
- data/src/TextBuilder.cpp +35 -21
- data/src/TextBuilder.hpp +6 -9
- data/src/Texture.cpp +62 -80
- data/src/Texture.hpp +25 -23
- data/src/TiledDrawable.cpp +150 -0
- data/src/TiledDrawable.hpp +47 -0
- data/src/TimingApple.cpp +1 -1
- data/src/Transform.cpp +45 -50
- data/src/TransformStack.hpp +16 -16
- data/src/TrueTypeFont.cpp +59 -51
- data/src/TrueTypeFont.hpp +6 -7
- data/src/TrueTypeFontApple.cpp +28 -19
- data/src/TrueTypeFontUnix.cpp +27 -23
- data/src/TrueTypeFontWin.cpp +30 -30
- data/src/Utility.cpp +84 -21
- data/src/UtilityWin.cpp +45 -0
- data/src/Window.cpp +92 -142
- data/src/WindowUIKit.cpp +14 -14
- metadata +72 -31
- data/include/Gosu/IO.hpp +0 -254
- data/include/Gosu/ImageData.hpp +0 -53
- data/include/Gosu/Inspection.hpp +0 -7
- data/lib/gosu/patches.rb +0 -66
- data/lib/gosu/run.rb +0 -20
- data/lib/gosu/swig_patches.rb +0 -110
- data/src/BlockAllocator.cpp +0 -131
- data/src/BlockAllocator.hpp +0 -32
- data/src/DirectoriesApple.cpp +0 -69
- data/src/DirectoriesUnix.cpp +0 -46
- data/src/DirectoriesWin.cpp +0 -65
- data/src/EmptyImageData.hpp +0 -52
- data/src/FileUnix.cpp +0 -99
- data/src/FileWin.cpp +0 -88
- data/src/IO.cpp +0 -60
- data/src/Iconv.hpp +0 -51
- data/src/Inspection.cpp +0 -27
- data/src/LargeImageData.cpp +0 -215
- data/src/LargeImageData.hpp +0 -39
- data/src/Log.hpp +0 -19
- data/src/RubyGosu.cxx +0 -13100
- data/src/RubyGosu.h +0 -49
- data/src/WinUtility.cpp +0 -61
- data/src/WinUtility.hpp +0 -27
data/src/BinPacker.hpp
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <Gosu/Platform.hpp>
|
4
|
+
#include <Gosu/Utility.hpp>
|
5
|
+
#include <mutex>
|
6
|
+
#include <memory>
|
7
|
+
#include <vector>
|
8
|
+
|
9
|
+
namespace Gosu
|
10
|
+
{
|
11
|
+
/// This implements an allocator for 2D rects in a given "bin", which is typically an OpenGL
|
12
|
+
/// texture. It uses the GUILLOTINE-LAS-RM-BSSF algorithm because it has the best worst-case
|
13
|
+
/// performance in the PDF paper found here: https://github.com/juj/RectangleBinPack
|
14
|
+
/// (The extra complexity of the MAXRECTS algorithm did not seem to be worth it, and the Skyline
|
15
|
+
/// algorithm does not seem well-suited for scenarios where images are occasionally deleted.)
|
16
|
+
///
|
17
|
+
/// Note: This class cannot use stb_rect_pack.h because that uses an "offline" algorithm,
|
18
|
+
/// i.e. it requires all boxes to be allocated at the same time, which is not how Gosu games are
|
19
|
+
/// usually structured.
|
20
|
+
///
|
21
|
+
/// (This class is non-copyable because alloc returns shared_ptrs that reference this object.
|
22
|
+
/// Moving a BinPacker instance would lead to dangling pointers.)
|
23
|
+
class BinPacker : private Noncopyable
|
24
|
+
{
|
25
|
+
const int m_width, m_height;
|
26
|
+
std::vector<Rect> m_free_rects;
|
27
|
+
std::mutex m_mutex;
|
28
|
+
|
29
|
+
public:
|
30
|
+
BinPacker(int width, int height);
|
31
|
+
|
32
|
+
int width() const { return m_width; }
|
33
|
+
int height() const { return m_height; }
|
34
|
+
|
35
|
+
/// Finds a free rectangle in the bin and marks it as used, or returns nullptr.
|
36
|
+
/// The returned shared_ptr will automatically mark the rectangle as freed through its
|
37
|
+
/// deleter. The shared_ptr must not outlive the BinPacker.
|
38
|
+
std::shared_ptr<const Rect> alloc(int width, int height);
|
39
|
+
/// Marks a previously allocated rectangle as free again. This must be called with one of
|
40
|
+
/// the rectangles previously returned by alloc().
|
41
|
+
void add_free_rect(const Rect& rect);
|
42
|
+
|
43
|
+
private:
|
44
|
+
/// Finds the best free rectangle using the "Best Short Side Fit" ("BSSF") metric, if any.
|
45
|
+
const Rect* best_free_rect(int width, int height) const;
|
46
|
+
|
47
|
+
/// Removes m_free_rects[index]. If a pointer to another index is given, it will be adjusted
|
48
|
+
/// to the new index of the previously pointed-to rectangle (if it has moved).
|
49
|
+
void remove_free_rect(int index, int* other_index = nullptr);
|
50
|
+
|
51
|
+
/// Performs the "Rectangle Merge Improvement" (-RM) by repeatedly merging adjacent free
|
52
|
+
/// rects into m_free_rects[index] if they can be replaced by a single, larger rectangle.
|
53
|
+
void merge_neighbors(int index);
|
54
|
+
};
|
55
|
+
}
|
data/src/Bitmap.cpp
CHANGED
@@ -1,200 +1,222 @@
|
|
1
1
|
#include <Gosu/Bitmap.hpp>
|
2
2
|
#include <Gosu/GraphicsBase.hpp>
|
3
|
+
#include <algorithm> // for std::equal, std::fill_n
|
4
|
+
#include <cstring> // for std::memcpy
|
5
|
+
#include <limits>
|
3
6
|
#include <stdexcept> // for std::invalid_argument
|
4
|
-
#include <utility>
|
7
|
+
#include <utility> // for std::move, std::swap
|
5
8
|
|
6
|
-
Gosu::Bitmap::Bitmap(int width, int height,
|
9
|
+
Gosu::Bitmap::Bitmap(int width, int height, Color c)
|
10
|
+
: m_width(width),
|
11
|
+
m_height(height)
|
7
12
|
{
|
8
|
-
|
13
|
+
if (width < 0 || height < 0) {
|
14
|
+
throw std::invalid_argument("Negative Gosu::Bitmap size");
|
15
|
+
}
|
16
|
+
|
17
|
+
// Don't allow bitmaps where there are more than INT_MAX pixels.
|
18
|
+
if (height != 0 && width > std::numeric_limits<int>::max() / height) {
|
19
|
+
throw std::invalid_argument("Gosu::Bitmap size out of bounds");
|
20
|
+
}
|
21
|
+
|
22
|
+
const int size = width * height;
|
23
|
+
m_pixels = Buffer(size * sizeof(Color));
|
24
|
+
std::fill_n(data(), size, c);
|
9
25
|
}
|
10
26
|
|
11
|
-
|
27
|
+
Gosu::Bitmap::Bitmap(int width, int height, Gosu::Buffer&& buffer)
|
28
|
+
: m_width(width),
|
29
|
+
m_height(height),
|
30
|
+
m_pixels(std::move(buffer))
|
12
31
|
{
|
13
|
-
|
14
|
-
std::
|
15
|
-
|
32
|
+
int pixels = width * height;
|
33
|
+
if (static_cast<std::size_t>(pixels) * sizeof(Color) != m_pixels.size()) {
|
34
|
+
throw std::length_error("Gosu::Bitmap given Gosu::Buffer of wrong size, expected "
|
35
|
+
+ std::to_string(pixels * sizeof(Color)) + ", given "
|
36
|
+
+ std::to_string(m_pixels.size()));
|
37
|
+
}
|
16
38
|
}
|
17
39
|
|
18
40
|
void Gosu::Bitmap::resize(int width, int height, Color c)
|
19
41
|
{
|
20
|
-
if (width
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
temp.m_width = width;
|
26
|
-
temp.m_height = height;
|
27
|
-
temp.m_pixels.resize(width * height, c);
|
28
|
-
temp.insert(0, 0, *this);
|
29
|
-
swap(temp);
|
42
|
+
if (width != m_width || height != m_height) {
|
43
|
+
Bitmap temp(width, height, c);
|
44
|
+
temp.insert(*this, 0, 0);
|
45
|
+
std::swap(*this, temp);
|
46
|
+
}
|
30
47
|
}
|
31
48
|
|
32
49
|
void Gosu::Bitmap::blend_pixel(int x, int y, Color c)
|
33
50
|
{
|
34
|
-
if (c.alpha == 0)
|
51
|
+
if (c.alpha == 0) {
|
52
|
+
return;
|
53
|
+
}
|
35
54
|
|
36
|
-
Color out =
|
37
|
-
if (out.alpha == 0) {
|
38
|
-
|
55
|
+
Color& out = pixel(x, y);
|
56
|
+
if (out.alpha == 0 || c.alpha == 255) {
|
57
|
+
out = c;
|
39
58
|
return;
|
40
59
|
}
|
41
60
|
|
42
61
|
int inv_alpha = out.alpha * (255 - c.alpha) / 255;
|
43
62
|
|
44
63
|
out.alpha = (c.alpha + inv_alpha);
|
45
|
-
out.red =
|
64
|
+
out.red = ((c.red * c.alpha + out.red * inv_alpha) / out.alpha);
|
46
65
|
out.green = ((c.green * c.alpha + out.green * inv_alpha) / out.alpha);
|
47
|
-
out.blue =
|
48
|
-
|
49
|
-
set_pixel(x, y, out);
|
66
|
+
out.blue = ((c.blue * c.alpha + out.blue * inv_alpha) / out.alpha);
|
50
67
|
}
|
51
68
|
|
52
|
-
void Gosu::Bitmap::insert(int x, int y
|
69
|
+
void Gosu::Bitmap::insert(const Bitmap& source, int x, int y)
|
53
70
|
{
|
54
|
-
insert(
|
71
|
+
insert(source, x, y, Rect::covering(source));
|
55
72
|
}
|
56
73
|
|
57
|
-
void Gosu::Bitmap::insert(int x, int y,
|
58
|
-
int src_x, int src_y, int src_width, int src_height)
|
74
|
+
void Gosu::Bitmap::insert(const Bitmap& source, int x, int y, Rect source_rect)
|
59
75
|
{
|
60
|
-
|
61
|
-
|
62
|
-
if (x < 0) {
|
63
|
-
int clip_left = -x;
|
64
|
-
|
65
|
-
if (clip_left >= src_width) return;
|
66
|
-
|
67
|
-
src_x += clip_left;
|
68
|
-
src_width -= clip_left;
|
69
|
-
x = 0;
|
76
|
+
if (&source == this) {
|
77
|
+
throw std::invalid_argument("Gosu::Bitmap::insert cannot copy parts of itself");
|
70
78
|
}
|
71
79
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
if (clip_top >= src_height) return;
|
80
|
+
// Make sure that the source area does not exceed the source image.
|
81
|
+
// If we need to move the source_rect origin, then also move the target rectangle origin.
|
82
|
+
source_rect.clip_to(Rect::covering(source), &x, &y);
|
76
83
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
if (x + src_width > m_width) {
|
83
|
-
if (x >= m_width) return;
|
84
|
+
// Set up the target area and make sure that it does not exceed this image.
|
85
|
+
Rect target_rect { .x = x, .y = y, .width = source_rect.width, .height = source_rect.height };
|
86
|
+
// If we need to move the target_rect origin, then also move the source_rect origin.
|
87
|
+
target_rect.clip_to(Rect::covering(*this), &source_rect.x, &source_rect.y);
|
84
88
|
|
85
|
-
|
86
|
-
|
89
|
+
// These are the first source and first destination pixels/rows.
|
90
|
+
Color* target_ptr = &pixel(target_rect.x, target_rect.y);
|
91
|
+
const Color* source_ptr = &source.pixel(source_rect.x, source_rect.y);
|
87
92
|
|
88
|
-
|
89
|
-
if (y >= m_height) return;
|
93
|
+
// target_rect might be smaller than source_rect now, so use its width/height for copying.
|
90
94
|
|
91
|
-
|
95
|
+
if (width() == source.width() && width() == target_rect.width) {
|
96
|
+
// If both images have the same width, and we want to copy full lines, then we can use a
|
97
|
+
// single memcpy for the whole operation. This is especially likely if we vertically resize
|
98
|
+
// a bitmap.
|
99
|
+
const int size = target_rect.width * target_rect.height;
|
100
|
+
std::memcpy(target_ptr, source_ptr, static_cast<std::size_t>(size) * sizeof(Color));
|
92
101
|
}
|
93
|
-
|
94
|
-
|
95
|
-
for (int
|
96
|
-
|
102
|
+
else {
|
103
|
+
// Otherwise, we need to copy line by line.
|
104
|
+
for (int row = 0; row < target_rect.height; ++row) {
|
105
|
+
std::memcpy(target_ptr, source_ptr, target_rect.width * sizeof(Color));
|
106
|
+
target_ptr += width();
|
107
|
+
source_ptr += source.width();
|
97
108
|
}
|
98
109
|
}
|
99
110
|
}
|
100
111
|
|
101
|
-
|
112
|
+
bool Gosu::Bitmap::operator==(const Gosu::Bitmap& other) const
|
102
113
|
{
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
// Calculate the average R/G/B of adjacent, non-transparent pixels.
|
107
|
-
unsigned neighbors = 0, red = 0, green = 0, blue = 0;
|
108
|
-
auto visit = [&](Color c) {
|
109
|
-
if (c != key) {
|
110
|
-
neighbors += 1;
|
111
|
-
red += c.red;
|
112
|
-
green += c.green;
|
113
|
-
blue += c.blue;
|
114
|
-
}
|
115
|
-
};
|
116
|
-
|
117
|
-
if (x > 0) visit(bitmap.get_pixel(x - 1, y));
|
118
|
-
if (x < bitmap.width() - 1) visit(bitmap.get_pixel(x + 1, y));
|
119
|
-
if (y > 0) visit(bitmap.get_pixel(x, y - 1));
|
120
|
-
if (y < bitmap.height() - 1) visit(bitmap.get_pixel(x, y + 1));
|
121
|
-
|
122
|
-
Color replacement = Color::NONE;
|
123
|
-
if (neighbors > 0) {
|
124
|
-
replacement.red = red / neighbors;
|
125
|
-
replacement.green = green / neighbors;
|
126
|
-
replacement.blue = blue / neighbors;
|
127
|
-
}
|
128
|
-
bitmap.set_pixel(x, y, replacement);
|
129
|
-
}
|
130
|
-
}
|
131
|
-
}
|
114
|
+
int pixels = width() * height();
|
115
|
+
return pixels == other.width() * other.height()
|
116
|
+
&& std::equal(data(), data() + pixels, other.data());
|
132
117
|
}
|
133
118
|
|
134
|
-
Gosu::Bitmap
|
135
|
-
int src_x, int src_y, int src_width, int src_height)
|
119
|
+
void Gosu::Bitmap::apply_color_key(Color key)
|
136
120
|
{
|
137
|
-
//
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
121
|
+
// The valid memory range from which the loop below can read.
|
122
|
+
const int pixels = width() * height();
|
123
|
+
Color* begin = data();
|
124
|
+
Color* end = begin + pixels;
|
125
|
+
|
126
|
+
for (Color* c = begin; c != end;) {
|
127
|
+
for (int x = 0; x < width(); ++x, ++c) {
|
128
|
+
// All colors except the color key should stay as they are.
|
129
|
+
if (*c != key) {
|
130
|
+
continue;
|
131
|
+
}
|
148
132
|
|
149
|
-
|
133
|
+
unsigned neighbors = 0, red = 0, green = 0, blue = 0;
|
150
134
|
|
151
|
-
|
152
|
-
|
135
|
+
const auto visit = [&](const Color* neighbor) {
|
136
|
+
if (neighbor >= begin && neighbor < end && *neighbor != key && neighbor->alpha) {
|
137
|
+
// Ignore other pixels that are or were equal to the color key.
|
138
|
+
neighbors += 1;
|
139
|
+
red += neighbor->red;
|
140
|
+
green += neighbor->green;
|
141
|
+
blue += neighbor->blue;
|
142
|
+
}
|
143
|
+
};
|
144
|
+
// Don't look at (x-1) pixels in the first column because we might accidentally use
|
145
|
+
// the pixels of the last column through wraparound.
|
146
|
+
if (x != 0) {
|
147
|
+
visit(c - width() - 1);
|
148
|
+
visit(c - 1);
|
149
|
+
visit(c + width() - 1);
|
150
|
+
}
|
151
|
+
visit(c - width());
|
152
|
+
visit(c + width());
|
153
|
+
// Don't look at (x+1) pixels in the last column because we might accidentally use
|
154
|
+
// the pixels of the first column through wraparound.
|
155
|
+
if (x != width() - 1) {
|
156
|
+
visit(c - width() + 1);
|
157
|
+
visit(c + 1);
|
158
|
+
visit(c + width() + 1);
|
159
|
+
}
|
153
160
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
dest.insert(1, dest.height() - 1,
|
162
|
-
source, src_x, src_y + src_height - 1, src_width, 1);
|
163
|
-
}
|
164
|
-
// Left.
|
165
|
-
if (image_flags & IF_TILEABLE_LEFT) {
|
166
|
-
dest.insert(0, 1,
|
167
|
-
source, src_x, src_y, 1, src_height);
|
168
|
-
}
|
169
|
-
// Right.
|
170
|
-
if (image_flags & IF_TILEABLE_RIGHT) {
|
171
|
-
dest.insert(dest.width() - 1, 1,
|
172
|
-
source, src_x + src_width - 1, src_y, 1, src_height);
|
161
|
+
*c = Color::NONE;
|
162
|
+
if (neighbors > 0) {
|
163
|
+
c->red = red / neighbors;
|
164
|
+
c->green = green / neighbors;
|
165
|
+
c->blue = blue / neighbors;
|
166
|
+
}
|
167
|
+
}
|
173
168
|
}
|
169
|
+
}
|
174
170
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
171
|
+
Gosu::Bitmap Gosu::apply_border_flags(unsigned image_flags, const Bitmap& source, Rect source_rect)
|
172
|
+
{
|
173
|
+
// Add one extra pixel around all four sides of the image.
|
174
|
+
Gosu::Bitmap result(source_rect.width + 2, source_rect.height + 2);
|
175
|
+
result.insert(source, 1, 1, source_rect);
|
176
|
+
|
177
|
+
// Now duplicate the edges of the image on all four sides.
|
178
|
+
const Rect top { source_rect.x, source_rect.y, source_rect.width, 1 };
|
179
|
+
result.insert(source, 1, 0, top);
|
180
|
+
const Rect bottom { source_rect.x, source_rect.bottom() - 1, source_rect.width, 1 };
|
181
|
+
result.insert(source, 1, result.height() - 1, bottom);
|
182
|
+
const Rect left { source_rect.x, source_rect.y, 1, source_rect.height };
|
183
|
+
result.insert(source, 0, 1, left);
|
184
|
+
const Rect right { source_rect.right() - 1, source_rect.y, 1, source_rect.height };
|
185
|
+
result.insert(source, result.width() - 1, 1, right);
|
186
|
+
// Also duplicate the corners of each size.
|
187
|
+
const Rect top_left { source_rect.x, source_rect.y, 1, 1 };
|
188
|
+
result.insert(source, 0, 0, top_left);
|
189
|
+
const Rect top_right { source_rect.right() - 1, source_rect.y, 1, 1 };
|
190
|
+
result.insert(source, result.width() - 1, 0, top_right);
|
191
|
+
const Rect bottom_left { source_rect.x, source_rect.bottom() - 1, 1, 1 };
|
192
|
+
result.insert(source, 0, result.height() - 1, bottom_left);
|
193
|
+
const Rect bottom_right { source_rect.right() - 1, source_rect.bottom() - 1, 1, 1 };
|
194
|
+
result.insert(source, result.width() - 1, result.height() - 1, bottom_right);
|
195
|
+
|
196
|
+
// On edges which are supposed to have tileable borders, we are now finished.
|
197
|
+
// Where soft borders are desired, we need to make all pixels on a side fully transparent.
|
198
|
+
if ((image_flags & IF_TILEABLE_TOP) == 0) {
|
199
|
+
for (int x = 0; x < result.width(); ++x) {
|
200
|
+
result.pixel(x, 0).alpha = 0;
|
201
|
+
}
|
179
202
|
}
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
203
|
+
if ((image_flags & IF_TILEABLE_BOTTOM) == 0) {
|
204
|
+
const int y = result.height() - 1;
|
205
|
+
for (int x = 0; x < result.width(); ++x) {
|
206
|
+
result.pixel(x, y).alpha = 0;
|
207
|
+
}
|
184
208
|
}
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
209
|
+
if ((image_flags & IF_TILEABLE_LEFT) == 0) {
|
210
|
+
for (int y = 0; y < result.height(); ++y) {
|
211
|
+
result.pixel(0, y).alpha = 0;
|
212
|
+
}
|
189
213
|
}
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
214
|
+
if ((image_flags & IF_TILEABLE_RIGHT) == 0) {
|
215
|
+
const int x = result.width() - 1;
|
216
|
+
for (int y = 0; y < result.height(); ++y) {
|
217
|
+
result.pixel(x, y).alpha = 0;
|
218
|
+
}
|
194
219
|
}
|
195
220
|
|
196
|
-
|
197
|
-
dest.insert(1, 1,
|
198
|
-
source, src_x, src_y, src_width, src_height);
|
199
|
-
return dest;
|
221
|
+
return result;
|
200
222
|
}
|
data/src/BitmapIO.cpp
CHANGED
@@ -1,94 +1,63 @@
|
|
1
1
|
#include <Gosu/Bitmap.hpp>
|
2
2
|
#include <Gosu/Utility.hpp>
|
3
|
-
#include <cstring> // for std::memcpy, std::size_t
|
4
3
|
#include <stdexcept> // for std::runtime_error
|
5
4
|
|
6
5
|
#define STB_IMAGE_IMPLEMENTATION
|
7
6
|
#define STBI_NO_STDIO
|
8
|
-
#define STBI_NO_LINEAR
|
7
|
+
#define STBI_NO_LINEAR // we don't really care about this, but it avoids warnings
|
9
8
|
|
10
9
|
#include <stb_image.h>
|
11
10
|
|
12
|
-
|
11
|
+
namespace
|
13
12
|
{
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
{
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
{
|
35
|
-
std::size_t remaining = reader.resource().size() - reader.position();
|
36
|
-
if (remaining < 2) return false;
|
37
|
-
char magic_bytes[2];
|
38
|
-
reader.read(&magic_bytes, sizeof magic_bytes);
|
39
|
-
return magic_bytes[0] == 'B' && magic_bytes[1] == 'M';
|
40
|
-
}
|
41
|
-
|
42
|
-
/// Returns a copy of the given Gosu::Image with the alpha channel removed.
|
43
|
-
/// Every pixel with alpha==0 will be replaced by Gosu::Color::FUCHSIA.
|
44
|
-
static std::vector<Gosu::Color::Channel> bitmap_to_rgb(const Gosu::Bitmap& bmp)
|
45
|
-
{
|
46
|
-
std::vector<Gosu::Color::Channel> rgb(static_cast<std::size_t>(bmp.width() * bmp.height() * 3));
|
47
|
-
for (std::size_t offset = 0; offset < rgb.size(); offset += 3) {
|
48
|
-
const Gosu::Color& color = bmp.data()[offset / 3];
|
49
|
-
if (color.alpha == 0) {
|
50
|
-
rgb[offset + 0] = 0xff;
|
51
|
-
rgb[offset + 1] = 0x00;
|
52
|
-
rgb[offset + 2] = 0xff;
|
53
|
-
} else {
|
54
|
-
rgb[offset + 0] = color.red;
|
55
|
-
rgb[offset + 1] = color.green;
|
56
|
-
rgb[offset + 2] = color.blue;
|
13
|
+
constexpr int JPEG_QUALITY = 80;
|
14
|
+
|
15
|
+
/// Returns a copy of the given Gosu::Image with the alpha channel removed.
|
16
|
+
/// Every pixel with alpha==0 will be replaced by Gosu::Color::FUCHSIA.
|
17
|
+
std::vector<Gosu::Color::Channel> remove_alpha_channel(const Gosu::Bitmap& bmp)
|
18
|
+
{
|
19
|
+
std::vector<Gosu::Color::Channel> rgb(
|
20
|
+
static_cast<std::size_t>(bmp.width() * bmp.height() * 3));
|
21
|
+
for (std::size_t offset = 0; offset < rgb.size(); offset += 3) {
|
22
|
+
const Gosu::Color& color = bmp.data()[offset / 3];
|
23
|
+
if (color.alpha == 0) {
|
24
|
+
rgb[offset + 0] = 0xff;
|
25
|
+
rgb[offset + 1] = 0x00;
|
26
|
+
rgb[offset + 2] = 0xff;
|
27
|
+
}
|
28
|
+
else {
|
29
|
+
rgb[offset + 0] = color.red;
|
30
|
+
rgb[offset + 1] = color.green;
|
31
|
+
rgb[offset + 2] = color.blue;
|
32
|
+
}
|
57
33
|
}
|
34
|
+
return rgb;
|
58
35
|
}
|
59
|
-
return rgb;
|
60
36
|
}
|
61
37
|
|
62
38
|
Gosu::Bitmap Gosu::load_image_file(const std::string& filename)
|
63
39
|
{
|
64
|
-
|
65
|
-
load_file(buffer, filename);
|
66
|
-
return load_image_file(buffer.front_reader());
|
40
|
+
return load_image(load_file(filename));
|
67
41
|
}
|
68
42
|
|
69
|
-
Gosu::Bitmap Gosu::
|
43
|
+
Gosu::Bitmap Gosu::load_image(const Buffer& buffer)
|
70
44
|
{
|
71
|
-
bool needs_color_key = is_bmp(input);
|
72
|
-
|
73
|
-
stbi_io_callbacks callbacks{};
|
74
|
-
callbacks.read = read_callback;
|
75
|
-
callbacks.skip = skip_callback;
|
76
|
-
callbacks.eof = eof_callback;
|
77
45
|
int x = 0, y = 0, n = 0;
|
78
|
-
|
79
|
-
|
46
|
+
stbi_uc* bytes = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &x, &y,
|
47
|
+
&n, STBI_rgb_alpha);
|
80
48
|
if (bytes == nullptr) {
|
81
|
-
throw std::runtime_error
|
49
|
+
throw std::runtime_error("Cannot load image: " + std::string(stbi_failure_reason()));
|
82
50
|
}
|
83
51
|
|
84
|
-
|
85
|
-
std::
|
86
|
-
|
87
|
-
stbi_image_free(bytes);
|
52
|
+
int pixels = x * y;
|
53
|
+
Buffer pixel_buffer(bytes, static_cast<std::size_t>(pixels) * sizeof(Color), &stbi_image_free);
|
54
|
+
Gosu::Bitmap bitmap(x, y, std::move(pixel_buffer));
|
88
55
|
|
89
|
-
|
90
|
-
|
56
|
+
// If we just read a BMP file, we want to apply a color key.
|
57
|
+
if (buffer.size() > 2 && buffer.data()[0] == 'B' && buffer.data()[1] == 'M') {
|
58
|
+
bitmap.apply_color_key(Gosu::Color::FUCHSIA);
|
91
59
|
}
|
60
|
+
|
92
61
|
return bitmap;
|
93
62
|
}
|
94
63
|
|
@@ -101,10 +70,11 @@ void Gosu::save_image_file(const Gosu::Bitmap& bitmap, const std::string& filena
|
|
101
70
|
|
102
71
|
if (has_extension(filename, "bmp")) {
|
103
72
|
ok = stbi_write_bmp(filename.c_str(), bitmap.width(), bitmap.height(), 3,
|
104
|
-
|
73
|
+
remove_alpha_channel(bitmap).data());
|
105
74
|
}
|
106
|
-
else if (has_extension(filename, "
|
107
|
-
ok =
|
75
|
+
else if (has_extension(filename, "jpg") || has_extension(filename, "jpeg")) {
|
76
|
+
ok = stbi_write_jpg(filename.c_str(), bitmap.width(), bitmap.height(), 3,
|
77
|
+
remove_alpha_channel(bitmap).data(), JPEG_QUALITY);
|
108
78
|
}
|
109
79
|
else {
|
110
80
|
ok = stbi_write_png(filename.c_str(), bitmap.width(), bitmap.height(), 4, bitmap.data(), 0);
|
@@ -115,32 +85,36 @@ void Gosu::save_image_file(const Gosu::Bitmap& bitmap, const std::string& filena
|
|
115
85
|
}
|
116
86
|
}
|
117
87
|
|
118
|
-
|
119
|
-
{
|
120
|
-
static_cast<Gosu::Writer*>(context)->write(data, size);
|
121
|
-
}
|
122
|
-
|
123
|
-
void Gosu::save_image_file(const Gosu::Bitmap& bitmap, Gosu::Writer writer,
|
124
|
-
const std::string_view& format_hint)
|
88
|
+
Gosu::Buffer Gosu::save_image(const Gosu::Bitmap& bitmap, std::string_view format_hint)
|
125
89
|
{
|
90
|
+
const auto stbi_write_to_vector = [](void* context, void* data, int size) {
|
91
|
+
auto* vec = static_cast<std::vector<std::uint8_t>*>(context);
|
92
|
+
const auto* begin = static_cast<const std::uint8_t*>(data);
|
93
|
+
const auto* end = begin + size;
|
94
|
+
vec->insert(vec->end(), begin, end);
|
95
|
+
};
|
96
|
+
|
97
|
+
std::vector<std::uint8_t> vector;
|
126
98
|
int ok;
|
127
99
|
|
128
|
-
if (has_extension(format_hint, "bmp")) {
|
129
|
-
ok = stbi_write_bmp_to_func(
|
130
|
-
3,
|
100
|
+
if (format_hint == "bmp" || has_extension(format_hint, "bmp")) {
|
101
|
+
ok = stbi_write_bmp_to_func(stbi_write_to_vector, &vector, bitmap.width(), bitmap.height(),
|
102
|
+
3, remove_alpha_channel(bitmap).data());
|
131
103
|
}
|
132
|
-
else if (has_extension(format_hint, "
|
133
|
-
|
134
|
-
ok =
|
135
|
-
|
104
|
+
else if (format_hint == "jpg" || format_hint == "jpeg" || has_extension(format_hint, "jpg")
|
105
|
+
|| has_extension(format_hint, "jpeg")) {
|
106
|
+
ok = stbi_write_jpg_to_func(stbi_write_to_vector, &vector, bitmap.width(), bitmap.height(),
|
107
|
+
3, remove_alpha_channel(bitmap).data(), JPEG_QUALITY);
|
136
108
|
}
|
137
109
|
else {
|
138
|
-
ok = stbi_write_png_to_func(
|
110
|
+
ok = stbi_write_png_to_func(stbi_write_to_vector, &vector, bitmap.width(), bitmap.height(),
|
139
111
|
4, bitmap.data(), 0);
|
140
112
|
}
|
141
113
|
|
142
114
|
if (ok == 0) {
|
143
|
-
throw std::runtime_error("Could not save image data to memory (format hint = '"
|
144
|
-
std::string
|
115
|
+
throw std::runtime_error("Could not save image data to memory (format hint = '"
|
116
|
+
+ std::string(format_hint) + "'");
|
145
117
|
}
|
118
|
+
|
119
|
+
return Buffer(std::move(vector));
|
146
120
|
}
|