summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/animation.cpp35
-rw-r--r--src/animation.h100
-rw-r--r--src/component.h11
-rw-r--r--src/components/ai.cpp142
-rw-r--r--src/components/ai.h73
-rw-r--r--src/components/animatable.h93
-rw-r--r--src/components/automatable.h34
-rw-r--r--src/components/controllable.h92
-rw-r--r--src/components/map_collision.cpp241
-rw-r--r--src/components/map_collision.h47
-rw-r--r--src/components/map_render.cpp40
-rw-r--r--src/components/map_render.h19
-rw-r--r--src/components/mappable.h152
-rw-r--r--src/components/orientable.h69
-rw-r--r--src/components/physics_body.cpp66
-rw-r--r--src/components/physics_body.h20
-rw-r--r--src/components/playable.h35
-rw-r--r--src/components/player_physics.cpp118
-rw-r--r--src/components/player_physics.h25
-rw-r--r--src/components/player_sprite.cpp60
-rw-r--r--src/components/player_sprite.h23
-rw-r--r--src/components/ponderable.h131
-rw-r--r--src/components/prototypable.h23
-rw-r--r--src/components/runnable.h51
-rw-r--r--src/components/schedulable.h21
-rw-r--r--src/components/simple_collider.cpp14
-rw-r--r--src/components/simple_collider.h18
-rw-r--r--src/components/static_image.cpp11
-rw-r--r--src/components/static_image.h18
-rw-r--r--src/components/transformable.h35
-rw-r--r--src/components/user_movement.cpp100
-rw-r--r--src/components/user_movement.h19
-rw-r--r--src/consts.h13
-rw-r--r--src/direction.h11
-rw-r--r--src/entity.cpp46
-rw-r--r--src/entity.h64
-rw-r--r--src/entity_manager.cpp39
-rw-r--r--src/entity_manager.h230
-rw-r--r--src/entityfactory.cpp182
-rw-r--r--src/entityfactory.h15
-rw-r--r--src/game.cpp200
-rw-r--r--src/game.h89
-rw-r--r--src/main.cpp27
-rw-r--r--src/map.cpp151
-rw-r--r--src/map.h70
-rw-r--r--src/muxer.cpp61
-rw-r--r--src/renderer.cpp861
-rw-r--r--src/renderer.h37
-rw-r--r--src/renderer/gl.h7
-rw-r--r--src/renderer/mesh.cpp109
-rw-r--r--src/renderer/mesh.h62
-rw-r--r--src/renderer/renderer.cpp630
-rw-r--r--src/renderer/renderer.h114
-rw-r--r--src/renderer/shader.cpp84
-rw-r--r--src/renderer/shader.h58
-rw-r--r--src/renderer/texture.cpp120
-rw-r--r--src/renderer/texture.h52
-rw-r--r--src/renderer/wrappers.h273
-rw-r--r--src/stb_image.h6326
-rw-r--r--src/system.h55
-rw-r--r--src/system_manager.h68
-rw-r--r--src/systems/animating.cpp103
-rw-r--r--src/systems/animating.h25
-rw-r--r--src/systems/controlling.cpp136
-rw-r--r--src/systems/controlling.h27
-rw-r--r--src/systems/mapping.cpp187
-rw-r--r--src/systems/mapping.h19
-rw-r--r--src/systems/orienting.cpp230
-rw-r--r--src/systems/orienting.h35
-rw-r--r--src/systems/playing.cpp144
-rw-r--r--src/systems/playing.h25
-rw-r--r--src/systems/pondering.cpp916
-rw-r--r--src/systems/pondering.h81
-rw-r--r--src/systems/realizing.cpp438
-rw-r--r--src/systems/realizing.h86
-rw-r--r--src/systems/scheduling.cpp54
-rw-r--r--src/systems/scheduling.h22
-rw-r--r--src/systems/scripting.cpp257
-rw-r--r--src/systems/scripting.h34
-rw-r--r--src/util.cpp30
-rw-r--r--src/util.h41
-rw-r--r--src/vector.h113
-rw-r--r--src/world.cpp150
-rw-r--r--src/world.h21
84 files changed, 5950 insertions, 9214 deletions
diff --git a/src/animation.cpp b/src/animation.cpp new file mode 100644 index 0000000..31ba21f --- /dev/null +++ b/src/animation.cpp
@@ -0,0 +1,35 @@
1#include "animation.h"
2
3AnimationSet::AnimationSet(
4 Texture texture,
5 int frameWidth,
6 int frameHeight,
7 int framesAcross) :
8 texture_(std::move(texture)),
9 frameWidth_(frameWidth),
10 frameHeight_(frameHeight),
11 framesAcross_(framesAcross)
12{
13}
14
15void AnimationSet::emplaceAnimation(
16 std::string animation,
17 size_t firstFrame,
18 size_t numFrames,
19 size_t delay)
20{
21 animations_.emplace(
22 std::piecewise_construct,
23 std::make_tuple(animation),
24 std::make_tuple(firstFrame, numFrames, delay));
25}
26
27Rectangle AnimationSet::getFrameRect(int frame) const
28{
29 return {
30 frameWidth_ * (frame % framesAcross_),
31 frameHeight_ * (frame / framesAcross_),
32 frameWidth_,
33 frameHeight_
34 };
35}
diff --git a/src/animation.h b/src/animation.h new file mode 100644 index 0000000..58df616 --- /dev/null +++ b/src/animation.h
@@ -0,0 +1,100 @@
1#ifndef ANIMATION_H_74EB0901
2#define ANIMATION_H_74EB0901
3
4#include "renderer/texture.h"
5#include <string>
6#include <map>
7#include <stdexcept>
8
9class Animation {
10public:
11
12 Animation(
13 size_t firstFrame,
14 size_t numFrames,
15 size_t delay) :
16 firstFrame_(firstFrame),
17 numFrames_(numFrames),
18 delay_(delay)
19 {
20 }
21
22 inline size_t getFirstFrame() const
23 {
24 return firstFrame_;
25 }
26
27 inline size_t getNumFrames() const
28 {
29 return numFrames_;
30 }
31
32 inline size_t getDelay() const
33 {
34 return delay_;
35 }
36
37private:
38
39 size_t firstFrame_;
40 size_t numFrames_;
41 size_t delay_;
42};
43
44class AnimationSet {
45public:
46
47 AnimationSet(
48 Texture texture,
49 int frameWidth,
50 int frameHeight,
51 int framesAcross);
52
53 void emplaceAnimation(
54 std::string animation,
55 size_t firstFrame,
56 size_t numFrames,
57 size_t delay);
58
59 inline const Animation& getAnimation(std::string animation) const
60 {
61 if (!animations_.count(animation))
62 {
63 throw std::invalid_argument("Animation does not exist");
64 }
65
66 return animations_.at(animation);
67 }
68
69 inline const Texture& getTexture() const
70 {
71 return texture_;
72 }
73
74 inline int getFrameWidth() const
75 {
76 return frameWidth_;
77 }
78
79 inline int getFrameHeight() const
80 {
81 return frameHeight_;
82 }
83
84 inline int getFramesAcross() const
85 {
86 return framesAcross_;
87 }
88
89 Rectangle getFrameRect(int frame) const;
90
91private:
92
93 std::map<std::string, Animation> animations_;
94 Texture texture_;
95 int frameWidth_;
96 int frameHeight_;
97 int framesAcross_;
98};
99
100#endif /* end of include guard: ANIMATION_H_74EB0901 */
diff --git a/src/component.h b/src/component.h new file mode 100644 index 0000000..b81dbee --- /dev/null +++ b/src/component.h
@@ -0,0 +1,11 @@
1#ifndef COMPONENT_H_F0CE4573
2#define COMPONENT_H_F0CE4573
3
4class Component {
5public:
6
7 virtual ~Component() = default;
8
9};
10
11#endif /* end of include guard: COMPONENT_H_F0CE4573 */
diff --git a/src/components/ai.cpp b/src/components/ai.cpp deleted file mode 100644 index 9f8c764..0000000 --- a/src/components/ai.cpp +++ /dev/null
@@ -1,142 +0,0 @@
1#include "ai.h"
2#include <cstdlib>
3#include "entity.h"
4
5void AIActionContainer::addAction(std::shared_ptr<AIAction> action)
6{
7 actions.push_back(action);
8}
9
10void AIActionContainer::start(Game& game, Entity& entity)
11{
12 currentAction = begin(actions);
13
14 if (currentAction != end(actions))
15 {
16 (*currentAction)->start(game, entity);
17 }
18}
19
20void AIActionContainer::perform(Game& game, Entity& entity, double dt)
21{
22 if (!isDone())
23 {
24 (*currentAction)->perform(game, entity, dt);
25
26 if ((*currentAction)->isDone())
27 {
28 currentAction++;
29
30 if (!isDone())
31 {
32 (*currentAction)->start(game, entity);
33 }
34 }
35 }
36}
37
38bool AIActionContainer::isDone() const
39{
40 return currentAction == end(actions);
41}
42
43AI::AI(int chance)
44{
45 this->chance = chance;
46}
47
48int AI::getChance() const
49{
50 return chance;
51}
52
53AI& AIComponent::emplaceAI(int chance)
54{
55 maxChance += chance;
56 ais.emplace_back(chance);
57
58 return ais.back();
59}
60
61void AIComponent::tick(Game& game, Entity& entity, double dt)
62{
63 if (currentAI == nullptr)
64 {
65 int toChoose = rand() % maxChance;
66 for (auto& ai : ais)
67 {
68 if (toChoose < ai.getChance())
69 {
70 currentAI = &ai;
71 break;
72 } else {
73 toChoose -= ai.getChance();
74 }
75 }
76
77 if (currentAI != nullptr)
78 {
79 currentAI->start(game, entity);
80 }
81 }
82
83 if (currentAI != nullptr)
84 {
85 currentAI->perform(game, entity, dt);
86
87 if (currentAI->isDone())
88 {
89 currentAI = nullptr;
90 }
91 }
92}
93
94MoveAIAction::MoveAIAction(Direction dir, int len, int speed)
95{
96 this->dir = dir;
97 this->len = len;
98 this->speed = speed;
99}
100
101void MoveAIAction::start(Game& game, Entity& entity)
102{
103 remaining = len;
104}
105
106void MoveAIAction::perform(Game&, Entity& entity, double dt)
107{
108 double dist = dt * speed;
109 remaining -= dist;
110
111 switch (dir)
112 {
113 case Direction::Left:
114 {
115 entity.position.first -= dist;
116 break;
117 }
118
119 case Direction::Right:
120 {
121 entity.position.first += dist;
122 break;
123 }
124
125 case Direction::Up:
126 {
127 entity.position.second -= dist;
128 break;
129 }
130
131 case Direction::Down:
132 {
133 entity.position.second += dist;
134 break;
135 }
136 }
137}
138
139bool MoveAIAction::isDone() const
140{
141 return remaining <= 0.0;
142}
diff --git a/src/components/ai.h b/src/components/ai.h deleted file mode 100644 index 840283b..0000000 --- a/src/components/ai.h +++ /dev/null
@@ -1,73 +0,0 @@
1#ifndef AI_H
2#define AI_H
3
4#include <list>
5#include <map>
6#include <string>
7#include <memory>
8
9#include "entity.h"
10
11class AIAction {
12 public:
13 virtual void start(Game& game, Entity& entity) = 0;
14 virtual void perform(Game& game, Entity& entity, double dt) = 0;
15 virtual bool isDone() const = 0;
16};
17
18class AIActionContainer {
19 public:
20 void addAction(std::shared_ptr<AIAction> action);
21 virtual void start(Game& game, Entity& entity);
22 virtual void perform(Game& game, Entity& entity, double dt);
23 virtual bool isDone() const;
24
25 private:
26 std::list<std::shared_ptr<AIAction>> actions;
27 std::list<std::shared_ptr<AIAction>>::iterator currentAction {end(actions)};
28};
29
30class AI : public AIActionContainer {
31 public:
32 AI(int chance);
33
34 int getChance() const;
35
36 private:
37 int chance;
38};
39
40class AIComponent : public Component {
41 public:
42 AI& emplaceAI(int chance);
43 void tick(Game& game, Entity& entity, double dt);
44
45 private:
46 int maxChance = 0;
47 std::list<AI> ais;
48 AI* currentAI = nullptr;
49};
50
51class MoveAIAction : public AIAction {
52 public:
53 enum class Direction {
54 Left,
55 Right,
56 Up,
57 Down
58 };
59
60 MoveAIAction(Direction dir, int len, int speed);
61
62 void start(Game& game, Entity& entity);
63 void perform(Game& game, Entity& entity, double dt);
64 bool isDone() const;
65
66 private:
67 Direction dir;
68 int len;
69 int speed;
70 double remaining;
71};
72
73#endif /* end of include guard: AI_H */
diff --git a/src/components/animatable.h b/src/components/animatable.h new file mode 100644 index 0000000..1a678ba --- /dev/null +++ b/src/components/animatable.h
@@ -0,0 +1,93 @@
1#ifndef SPRITE_RENDERABLE_H_D3AACBBF
2#define SPRITE_RENDERABLE_H_D3AACBBF
3
4#include "component.h"
5#include "animation.h"
6#include <string>
7
8class AnimatableComponent : public Component {
9public:
10
11 /**
12 * Constructor for initializing the animation set, because it is not default
13 * constructible.
14 */
15 AnimatableComponent(
16 AnimationSet animationSet) :
17 animationSet(std::move(animationSet))
18 {
19 }
20
21 /**
22 * The animation set that this entity will use -- an object describing the
23 * different animations that can be used to render the entity.
24 *
25 * @managed_by RealizingSystem
26 */
27 AnimationSet animationSet;
28
29 /**
30 * The name of the currently active animation.
31 *
32 * @managed_by AnimatingSystem
33 */
34 std::string animation;
35
36 /**
37 * For prototypes, the name of the original animation.
38 *
39 * @managed_by RealizingSystem
40 */
41 std::string origAnimation;
42
43 /**
44 * Helper method for accessing the currently active animation.
45 */
46 inline const Animation& getAnimation() const
47 {
48 return animationSet.getAnimation(animation);
49 }
50
51 /**
52 * The frame of animation that is currently being rendered.
53 *
54 * @managed_by AnimatingSystem
55 */
56 size_t frame = 0;
57
58 /**
59 * The amount of time (in game frames) before the animation is advanced.
60 *
61 * @managed_by AnimatingSystem
62 */
63 size_t countdown = 0;
64
65 /**
66 * This option allows to give the sprite a "flickering" effect (as in, it is
67 * not rendered in some frames).
68 */
69 bool flickering = false;
70
71 /**
72 * Used for the flickering effect.
73 *
74 * @managed_by AnimatingSystem
75 */
76 size_t flickerTimer = 0;
77
78 /**
79 * If enabled, this will prevent the sprite's animation from progressing (but
80 * will not affect things such as placement on screen and flickering).
81 */
82 bool frozen = false;
83
84 /**
85 * If this flag is disabled, the entity will be ignored by the animating
86 * system.
87 *
88 * @managed_by RealizingSystem
89 */
90 bool active = false;
91};
92
93#endif /* end of include guard: SPRITE_RENDERABLE_H_D3AACBBF */
diff --git a/src/components/automatable.h b/src/components/automatable.h new file mode 100644 index 0000000..22d9859 --- /dev/null +++ b/src/components/automatable.h
@@ -0,0 +1,34 @@
1#ifndef AUTOMATABLE_H_FACB42A5
2#define AUTOMATABLE_H_FACB42A5
3
4#include "component.h"
5#include "entity_manager.h"
6
7class AutomatableComponent : public Component {
8public:
9
10 using id_type = EntityManager::id_type;
11
12 /**
13 * Controls what script will be run as this entity's behavior. It should refer
14 * to a table in the global namespace of the script engine state, and that
15 * table should contain a function called "Behavior".
16 */
17 std::string table;
18
19 /**
20 * Whether or not the behavior script is running.
21 *
22 * @managed_by ScriptingSystem
23 */
24 bool running = false;
25
26 /**
27 * The entity ID of the running script, if there is one.
28 *
29 * @managed_by ScriptingSystem
30 */
31 id_type script;
32};
33
34#endif /* end of include guard: AUTOMATABLE_H_FACB42A5 */
diff --git a/src/components/controllable.h b/src/components/controllable.h new file mode 100644 index 0000000..1b12985 --- /dev/null +++ b/src/components/controllable.h
@@ -0,0 +1,92 @@
1#ifndef CONTROLLABLE_H_4E0B85B4
2#define CONTROLLABLE_H_4E0B85B4
3
4#include "component.h"
5#include "renderer/gl.h"
6
7class ControllableComponent : public Component {
8public:
9
10 inline int getLeftKey() const
11 {
12 return leftKey_;
13 }
14
15 inline void setLeftKey(int k)
16 {
17 leftKey_ = k;
18 }
19
20 inline int getRightKey() const
21 {
22 return rightKey_;
23 }
24
25 inline void setRightKey(int k)
26 {
27 rightKey_ = k;
28 }
29
30 inline int getJumpKey() const
31 {
32 return jumpKey_;
33 }
34
35 inline void setJumpKey(int k)
36 {
37 jumpKey_ = k;
38 }
39
40 inline int getDropKey() const
41 {
42 return dropKey_;
43 }
44
45 inline void setDropKey(int k)
46 {
47 dropKey_ = k;
48 }
49
50 inline bool isFrozen() const
51 {
52 return frozen_;
53 }
54
55 inline void setFrozen(bool f)
56 {
57 frozen_ = f;
58 }
59
60 inline bool isHoldingLeft() const
61 {
62 return holdingLeft_;
63 }
64
65 inline void setHoldingLeft(bool f)
66 {
67 holdingLeft_ = f;
68 }
69
70 inline bool isHoldingRight() const
71 {
72 return holdingRight_;
73 }
74
75 inline void setHoldingRight(bool f)
76 {
77 holdingRight_ = f;
78 }
79
80private:
81
82 int leftKey_ = GLFW_KEY_LEFT;
83 int rightKey_ = GLFW_KEY_RIGHT;
84 int jumpKey_ = GLFW_KEY_UP;
85 int dropKey_ = GLFW_KEY_DOWN;
86
87 bool frozen_ = false;
88 bool holdingLeft_ = false;
89 bool holdingRight_ = false;
90};
91
92#endif /* end of include guard: CONTROLLABLE_H_4E0B85B4 */
diff --git a/src/components/map_collision.cpp b/src/components/map_collision.cpp deleted file mode 100644 index 3ad574b..0000000 --- a/src/components/map_collision.cpp +++ /dev/null
@@ -1,241 +0,0 @@
1#include "map_collision.h"
2#include "map.h"
3#include "game.h"
4#include "consts.h"
5
6MapCollisionComponent::MapCollisionComponent(const Map& map) : map(map)
7{
8 addCollision(-6, 0, MAP_HEIGHT*TILE_HEIGHT, Direction::left, collisionFromMoveType(map.getAdjacent(Map::MoveDir::Left).type));
9 addCollision(GAME_WIDTH+6, 0, MAP_HEIGHT*TILE_HEIGHT, Direction::right, collisionFromMoveType(map.getAdjacent(Map::MoveDir::Right).type));
10 addCollision(-6, 0, GAME_WIDTH, Direction::up, collisionFromMoveType(map.getAdjacent(Map::MoveDir::Up).type));
11 addCollision(MAP_HEIGHT*TILE_HEIGHT+6, 0, GAME_WIDTH, Direction::down, collisionFromMoveType(map.getAdjacent(Map::MoveDir::Down).type));
12
13 for (int i=0; i<MAP_WIDTH*MAP_HEIGHT; i++)
14 {
15 int x = i % MAP_WIDTH;
16 int y = i / MAP_WIDTH;
17 int tile = map.getMapdata()[i];
18
19 if ((tile > 0) && (tile < 28) && (!((tile >= 5) && (tile <= 7))))
20 {
21 addCollision(x*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::right, Collision::Type::wall);
22 addCollision((x+1)*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::left, Collision::Type::wall);
23 addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::wall);
24 addCollision((y+1)*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::up, Collision::Type::wall);
25 } else if ((tile >= 5) && (tile <= 7))
26 {
27 addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::platform);
28 } else if (tile == 42)
29 {
30 addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::danger);
31 }
32 }
33}
34
35void MapCollisionComponent::addCollision(double axis, double lower, double
36 upper, Direction dir, Collision::Type type)
37{
38 std::list<Collision>::iterator it;
39
40 switch (dir)
41 {
42 case Direction::up:
43 it = up_collisions.begin();
44 for (; it!=up_collisions.end(); it++)
45 {
46 if (it->axis < axis) break;
47 }
48
49 up_collisions.insert(it, {axis, lower, upper, type});
50
51 break;
52 case Direction::down:
53 it = down_collisions.begin();
54 for (; it!=down_collisions.end(); it++)
55 {
56 if (it->axis > axis) break;
57 }
58
59 down_collisions.insert(it, {axis, lower, upper, type});
60
61 break;
62 case Direction::left:
63 it = left_collisions.begin();
64 for (; it!=left_collisions.end(); it++)
65 {
66 if (it->axis < axis) break;
67 }
68
69 left_collisions.insert(it, {axis, lower, upper, type});
70
71 break;
72 case Direction::right:
73 it = right_collisions.begin();
74 for (; it!=right_collisions.end(); it++)
75 {
76 if (it->axis > axis) break;
77 }
78
79 right_collisions.insert(it, {axis, lower, upper, type});
80
81 break;
82 }
83}
84
85void MapCollisionComponent::detectCollision(Game& game, Entity&, Entity& collider, std::pair<double, double> old_position)
86{
87 if (collider.position.first < old_position.first)
88 {
89 for (auto collision : left_collisions)
90 {
91 if (collision.axis > old_position.first) continue;
92 if (collision.axis < collider.position.first) break;
93
94 if ((old_position.second+collider.size.second > collision.lower) && (old_position.second < collision.upper))
95 {
96 // We have a collision!
97 processCollision(game, collider, collision, Direction::left, old_position);
98
99 break;
100 }
101 }
102 } else if (collider.position.first > old_position.first)
103 {
104 for (auto collision : right_collisions)
105 {
106 if (collision.axis < old_position.first+collider.size.first) continue;
107 if (collision.axis > collider.position.first+collider.size.first) break;
108
109 if ((old_position.second+collider.size.second > collision.lower) && (old_position.second < collision.upper))
110 {
111 // We have a collision!
112 processCollision(game, collider, collision, Direction::right, old_position);
113
114 break;
115 }
116 }
117 }
118
119 if (collider.position.second < old_position.second)
120 {
121 for (auto collision : up_collisions)
122 {
123 if (collision.axis > old_position.second) continue;
124 if (collision.axis < collider.position.second) break;
125
126 if ((collider.position.first+collider.size.first > collision.lower) && (collider.position.first < collision.upper))
127 {
128 // We have a collision!
129 processCollision(game, collider, collision, Direction::up, old_position);
130
131 break;
132 }
133 }
134 } else if (collider.position.second > old_position.second)
135 {
136 for (auto collision : down_collisions)
137 {
138 if (collision.axis < old_position.second+collider.size.second) continue;
139 if (collision.axis > collider.position.second+collider.size.second) break;
140
141 if ((collider.position.first+collider.size.first > collision.lower) && (collider.position.first < collision.upper))
142 {
143 // We have a collision!
144 processCollision(game, collider, collision, Direction::down, old_position);
145
146 break;
147 }
148 }
149 }
150}
151
152void MapCollisionComponent::processCollision(Game& game, Entity& collider, Collision collision, Direction dir, std::pair<double, double> old_position)
153{
154 if (collision.type == Collision::Type::wall)
155 {
156 if (dir == Direction::left)
157 {
158 collider.position.first = collision.axis;
159
160 Message msg(Message::Type::setHorizontalVelocity);
161 msg.velocity = 0.0;
162 collider.send(game, msg);
163 } else if (dir == Direction::right)
164 {
165 collider.position.first = collision.axis - collider.size.first;
166
167 Message msg(Message::Type::setHorizontalVelocity);
168 msg.velocity = 0.0;
169 collider.send(game, msg);
170 } else if (dir == Direction::up)
171 {
172 collider.position.second = collision.axis;
173
174 Message msg(Message::Type::setVerticalVelocity);
175 msg.velocity = 0.0;
176 collider.send(game, msg);
177 } else if (dir == Direction::down)
178 {
179 collider.position.second = collision.axis - collider.size.second;
180 collider.send(game, Message::Type::hitTheGround);
181 }
182 } else if (collision.type == Collision::Type::wrap)
183 {
184 if (dir == Direction::left)
185 {
186 collider.position.first = GAME_WIDTH-collider.size.first/2;
187 } else if (dir == Direction::right)
188 {
189 collider.position.first = -collider.size.first/2;
190 } else if (dir == Direction::up)
191 {
192 collider.position.second = GAME_HEIGHT-collider.size.second/2-1;
193 } else if (dir == Direction::down)
194 {
195 collider.position.second = -collider.size.second/2;
196 }
197 } else if (collision.type == Collision::Type::teleport)
198 {
199 if (dir == Direction::left)
200 {
201 game.loadMap(game.getWorld().getMap(map.getAdjacent(Map::MoveDir::Left).map), std::make_pair(GAME_WIDTH-collider.size.first/2, old_position.second));
202 } else if (dir == Direction::right)
203 {
204 game.loadMap(game.getWorld().getMap(map.getAdjacent(Map::MoveDir::Right).map), std::make_pair(-collider.size.first/2, old_position.second));
205 } else if (dir == Direction::up)
206 {
207 game.loadMap(game.getWorld().getMap(map.getAdjacent(Map::MoveDir::Up).map), std::make_pair(old_position.first, MAP_HEIGHT*TILE_HEIGHT-collider.size.second/2));
208 } else if (dir == Direction::down)
209 {
210 game.loadMap(game.getWorld().getMap(map.getAdjacent(Map::MoveDir::Down).map), std::make_pair(old_position.first, -collider.size.second/2));
211 }
212 } else if (collision.type == Collision::Type::reverse)
213 {
214 // TODO reverse
215 if (dir == Direction::right)
216 {
217 collider.position.first = collision.axis - collider.size.first;
218 collider.send(game, Message::Type::walkLeft);
219 }
220 } else if (collision.type == Collision::Type::platform)
221 {
222 Message msg(Message::Type::drop);
223 msg.dropAxis = collision.axis;
224
225 collider.send(game, msg);
226 } else if (collision.type == Collision::Type::danger)
227 {
228 game.playerDie();
229 }
230}
231
232MapCollisionComponent::Collision::Type MapCollisionComponent::collisionFromMoveType(Map::MoveType type)
233{
234 switch (type)
235 {
236 case Map::MoveType::Wall: return Collision::Type::wall;
237 case Map::MoveType::Wrap: return Collision::Type::wrap;
238 case Map::MoveType::Warp: return Collision::Type::teleport;
239 case Map::MoveType::ReverseWarp: return Collision::Type::reverse;
240 }
241}
diff --git a/src/components/map_collision.h b/src/components/map_collision.h deleted file mode 100644 index 18b9397..0000000 --- a/src/components/map_collision.h +++ /dev/null
@@ -1,47 +0,0 @@
1#ifndef MAP_COLLISION_H
2#define MAP_COLLISION_H
3
4#include "entity.h"
5#include "map.h"
6#include <list>
7
8class Game;
9
10class MapCollisionComponent : public Component {
11 public:
12 MapCollisionComponent(const Map& map);
13 void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position);
14
15 private:
16 enum class Direction {
17 up, left, down, right
18 };
19
20 struct Collision {
21 enum class Type {
22 wall,
23 wrap,
24 teleport,
25 reverse,
26 platform,
27 danger
28 };
29
30 double axis;
31 double lower;
32 double upper;
33 Type type;
34 };
35
36 void addCollision(double axis, double lower, double upper, Direction dir, Collision::Type type);
37 void processCollision(Game& game, Entity& collider, Collision collision, Direction dir, std::pair<double, double> old_position);
38 Collision::Type collisionFromMoveType(Map::MoveType type);
39
40 std::list<Collision> left_collisions;
41 std::list<Collision> right_collisions;
42 std::list<Collision> up_collisions;
43 std::list<Collision> down_collisions;
44 const Map& map;
45};
46
47#endif
diff --git a/src/components/map_render.cpp b/src/components/map_render.cpp deleted file mode 100644 index 45766e1..0000000 --- a/src/components/map_render.cpp +++ /dev/null
@@ -1,40 +0,0 @@
1#include "map_render.h"
2#include "map.h"
3#include "game.h"
4#include "consts.h"
5
6MapRenderComponent::MapRenderComponent(const Map& map) : screen(GAME_WIDTH, GAME_HEIGHT)
7{
8 screen.fill(screen.entirety(), 0, 0, 0);
9
10 Texture tiles("res/tiles.png");
11
12 for (int i=0; i<MAP_WIDTH*MAP_HEIGHT; i++)
13 {
14 int tile = map.getMapdata()[i];
15 int x = i % MAP_WIDTH;
16 int y = i / MAP_WIDTH;
17 Rectangle dst {x*TILE_WIDTH, y*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT};
18 Rectangle src {tile%8*TILE_WIDTH, tile/8*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT};
19
20 if (tile > 0)
21 {
22 screen.blit(tiles, src, dst);
23 }
24 }
25
26 Texture font("res/font.bmp");
27 std::string map_name = map.getTitle();
28 int start_x = (40/2) - (map_name.length()/2);
29 for (size_t i=0; i<map_name.length(); i++)
30 {
31 Rectangle srcRect {map_name[i] % 16 * 8, map_name[i] / 16 * 8, 8, 8};
32 Rectangle dstRect {(start_x + (int)i)*8, 24*8, 8, 8};
33 screen.blit(font, srcRect, dstRect);
34 }
35}
36
37void MapRenderComponent::render(Game&, Entity&, Texture& buffer)
38{
39 buffer.blit(screen, screen.entirety(), buffer.entirety());
40}
diff --git a/src/components/map_render.h b/src/components/map_render.h deleted file mode 100644 index a232aa6..0000000 --- a/src/components/map_render.h +++ /dev/null
@@ -1,19 +0,0 @@
1#ifndef MAP_RENDER_H
2#define MAP_RENDER_H
3
4#include "entity.h"
5#include "renderer.h"
6
7class Map;
8class Game;
9
10class MapRenderComponent : public Component {
11 public:
12 MapRenderComponent(const Map& map);
13 void render(Game& game, Entity& entity, Texture& buffer);
14
15 private:
16 Texture screen;
17};
18
19#endif
diff --git a/src/components/mappable.h b/src/components/mappable.h new file mode 100644 index 0000000..e92074e --- /dev/null +++ b/src/components/mappable.h
@@ -0,0 +1,152 @@
1#ifndef MAPPABLE_H_0B0316FB
2#define MAPPABLE_H_0B0316FB
3
4#include <map>
5#include <string>
6#include <vector>
7#include <list>
8#include "component.h"
9#include "renderer/texture.h"
10#include "components/ponderable.h"
11#include "entity_manager.h"
12
13class MappableComponent : public Component {
14public:
15
16 using id_type = EntityManager::id_type;
17
18 /**
19 * Helper type that stores information about map adjacency.
20 */
21 class Adjacent {
22 public:
23
24 enum class Type {
25 wall,
26 wrap,
27 warp,
28 reverse
29 };
30
31 Adjacent(
32 Type type = Type::wall,
33 size_t mapId = 0) :
34 type(type),
35 mapId(mapId)
36 {
37 }
38
39 Type type;
40 size_t mapId;
41 };
42
43 /**
44 * Helper type that stores information about collision boundaries.
45 */
46 class Boundary {
47 public:
48
49 using Type = PonderableComponent::Collision;
50
51 Boundary(
52 double axis,
53 double lower,
54 double upper,
55 Type type) :
56 axis(axis),
57 lower(lower),
58 upper(upper),
59 type(type)
60 {
61 }
62
63 double axis;
64 double lower;
65 double upper;
66 Type type;
67 };
68
69 /**
70 * Helper types for efficient storage and lookup of collision boundaries.
71 */
72 using asc_boundaries_type =
73 std::multimap<
74 double,
75 const Boundary,
76 std::less<double>>;
77
78 using desc_boundaries_type =
79 std::multimap<
80 double,
81 const Boundary,
82 std::greater<double>>;
83
84 /**
85 * Constructor for initializing the tileset and font attributes, as they are
86 * not default constructible.
87 */
88 MappableComponent(
89 Texture tileset,
90 Texture font) :
91 tileset(std::move(tileset)),
92 font(std::move(font))
93 {
94 }
95
96 /**
97 * The ID of the map in the world definition that this entity represents.
98 *
99 * @managed_by RealizingSystem
100 */
101 size_t mapId;
102
103 /**
104 * The title of the map, which is displayed at the bottom of the screen.
105 */
106 std::string title;
107
108 /**
109 * The map data.
110 *
111 * @managed_by RealizingSystem
112 */
113 std::vector<int> tiles;
114
115 /**
116 * These objects describe the behavior of the four edges of the map.
117 *
118 * @managed_by RealizingSystem
119 */
120 Adjacent leftAdjacent;
121 Adjacent rightAdjacent;
122 Adjacent upAdjacent;
123 Adjacent downAdjacent;
124
125 /**
126 * Collision boundaries, for detecting when a ponderable entity is colliding
127 * with the environment.
128 *
129 * @managed_by MappingSystem
130 */
131 desc_boundaries_type leftBoundaries;
132 asc_boundaries_type rightBoundaries;
133 desc_boundaries_type upBoundaries;
134 asc_boundaries_type downBoundaries;
135
136 /**
137 * The list of entities representing the objects owned by the map.
138 *
139 * @managed_by RealizingSystem
140 */
141 std::list<id_type> objects;
142
143 /**
144 * The tilesets for the map and the map name.
145 *
146 * TODO: These probably do not belong here.
147 */
148 Texture tileset;
149 Texture font;
150};
151
152#endif /* end of include guard: MAPPABLE_H_0B0316FB */
diff --git a/src/components/orientable.h b/src/components/orientable.h new file mode 100644 index 0000000..e356b78 --- /dev/null +++ b/src/components/orientable.h
@@ -0,0 +1,69 @@
1#ifndef ORIENTABLE_H_EDB6C4A1
2#define ORIENTABLE_H_EDB6C4A1
3
4#include "component.h"
5
6class OrientableComponent : public Component {
7public:
8
9 enum class WalkState {
10 still,
11 left,
12 right
13 };
14
15 enum class DropState {
16 none,
17 ready,
18 active
19 };
20
21 inline bool isFacingRight() const
22 {
23 return facingRight_;
24 }
25
26 inline void setFacingRight(bool v)
27 {
28 facingRight_ = v;
29 }
30
31 inline WalkState getWalkState() const
32 {
33 return walkState_;
34 }
35
36 inline void setWalkState(WalkState v)
37 {
38 walkState_ = v;
39 }
40
41 inline bool isJumping() const
42 {
43 return jumping_;
44 }
45
46 inline void setJumping(bool v)
47 {
48 jumping_ = v;
49 }
50
51 inline DropState getDropState() const
52 {
53 return dropState_;
54 }
55
56 inline void setDropState(DropState v)
57 {
58 dropState_ = v;
59 }
60
61private:
62
63 bool facingRight_ = false;
64 WalkState walkState_ = WalkState::still;
65 bool jumping_ = false;
66 DropState dropState_ = DropState::none;
67};
68
69#endif /* end of include guard: ORIENTABLE_H_EDB6C4A1 */
diff --git a/src/components/physics_body.cpp b/src/components/physics_body.cpp deleted file mode 100644 index 97394d1..0000000 --- a/src/components/physics_body.cpp +++ /dev/null
@@ -1,66 +0,0 @@
1#include "physics_body.h"
2#include "game.h"
3#include "consts.h"
4
5void PhysicsBodyComponent::receive(Game&, Entity&, const Message& msg)
6{
7 if (msg.type == Message::Type::walkLeft)
8 {
9 velocity.first = -90;
10 } else if (msg.type == Message::Type::walkRight)
11 {
12 velocity.first = 90;
13 } else if (msg.type == Message::Type::stopWalking)
14 {
15 velocity.first = 0.0;
16 } else if (msg.type == Message::Type::setHorizontalVelocity)
17 {
18 velocity.first = msg.velocity;
19 } else if (msg.type == Message::Type::setVerticalVelocity)
20 {
21 velocity.second = msg.velocity;
22 }
23}
24
25void PhysicsBodyComponent::tick(Game&, Entity& entity, double dt)
26{
27 // Accelerate
28 velocity.first += accel.first * dt;
29 velocity.second += accel.second * dt;
30
31 // Terminal velocity
32#define TERMINAL_VELOCITY_X (2 * TILE_WIDTH * FRAMES_PER_SECOND)
33#define TERMINAL_VELOCITY_Y (2 * TILE_HEIGHT * FRAMES_PER_SECOND)
34 if (velocity.first < -TERMINAL_VELOCITY_X) velocity.first = -TERMINAL_VELOCITY_X;
35 if (velocity.first > TERMINAL_VELOCITY_X) velocity.first = TERMINAL_VELOCITY_X;
36 if (velocity.second < -TERMINAL_VELOCITY_Y) velocity.second = -TERMINAL_VELOCITY_Y;
37 if (velocity.second > TERMINAL_VELOCITY_Y) velocity.second = TERMINAL_VELOCITY_Y;
38
39 // Do the movement
40 entity.position.first += velocity.first * dt;
41 entity.position.second += velocity.second * dt;
42}
43
44void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position)
45{
46 // If already colliding, do nothing!
47 if ((old_position.first + collider.size.first > entity.position.first)
48 && (old_position.first < entity.position.first + entity.size.first)
49 && (old_position.second + collider.size.second > entity.position.second)
50 && (old_position.second < entity.position.second + entity.size.second))
51 {
52 return;
53 }
54
55 // If newly colliding, SHOCK AND HORROR!
56 if ((collider.position.first + collider.size.first > entity.position.first)
57 && (collider.position.first < entity.position.first + entity.size.first)
58 && (collider.position.second + collider.size.second > entity.position.second)
59 && (collider.position.second < entity.position.second + entity.size.second))
60 {
61 Message msg(Message::Type::collision);
62 msg.collisionEntity = &collider;
63
64 entity.send(game, msg);
65 }
66}
diff --git a/src/components/physics_body.h b/src/components/physics_body.h deleted file mode 100644 index 079cc51..0000000 --- a/src/components/physics_body.h +++ /dev/null
@@ -1,20 +0,0 @@
1#ifndef PHYSICS_BODY_H
2#define PHYSICS_BODY_H
3
4#include "entity.h"
5#include <utility>
6
7class Game;
8
9class PhysicsBodyComponent : public Component {
10 public:
11 void receive(Game& game, Entity& entity, const Message& msg);
12 void tick(Game& game, Entity& entity, double dt);
13 void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position);
14
15 protected:
16 std::pair<double, double> velocity;
17 std::pair<double, double> accel;
18};
19
20#endif
diff --git a/src/components/playable.h b/src/components/playable.h new file mode 100644 index 0000000..7404f1f --- /dev/null +++ b/src/components/playable.h
@@ -0,0 +1,35 @@
1#ifndef PLAYABLE_H_DDC566C3
2#define PLAYABLE_H_DDC566C3
3
4#include "component.h"
5#include "entity_manager.h"
6#include "vector.h"
7
8class PlayableComponent : public Component {
9public:
10
11 using id_type = EntityManager::id_type;
12
13 /**
14 * The entity ID of the map that the player is on.
15 *
16 * @managed_by PlayingSystem
17 */
18 id_type mapId;
19
20 /**
21 * The map ID and coordinates of the location that the player will spawn after
22 * dying. Note that the map ID here is a world description map ID, not an
23 * entity ID.
24 *
25 * @managed_by PlayingSystem
26 */
27 size_t checkpointMapId;
28 vec2d checkpointPos;
29
30 bool checkpointMapObject = false;
31 size_t checkpointMapObjectIndex;
32
33};
34
35#endif /* end of include guard: PLAYABLE_H_DDC566C3 */
diff --git a/src/components/player_physics.cpp b/src/components/player_physics.cpp deleted file mode 100644 index 40e9948..0000000 --- a/src/components/player_physics.cpp +++ /dev/null
@@ -1,118 +0,0 @@
1#include "player_physics.h"
2#include "muxer.h"
3#include "game.h"
4#include "consts.h"
5
6#define JUMP_VELOCITY(h, l) (-2 * (h) / (l))
7#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l))
8
9PlayerPhysicsComponent::PlayerPhysicsComponent()
10{
11 jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3);
12 jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3);
13 jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233);
14
15 accel.second = jump_gravity_short;
16}
17
18void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg)
19{
20 if (msg.type == Message::Type::walkLeft)
21 {
22 velocity.first = -90;
23 direction = -1;
24 } else if (msg.type == Message::Type::walkRight)
25 {
26 velocity.first = 90;
27 direction = 1;
28 } else if (msg.type == Message::Type::stopWalking)
29 {
30 velocity.first = 0.0;
31 direction = 0;
32 } else if (msg.type == Message::Type::setHorizontalVelocity)
33 {
34 velocity.first = msg.velocity;
35 } else if (msg.type == Message::Type::setVerticalVelocity)
36 {
37 velocity.second = msg.velocity;
38 } else if (msg.type == Message::Type::hitTheGround)
39 {
40 if (isFalling)
41 {
42 playSound("res/Randomize27.wav", 0.05);
43 isFalling = false;
44 }
45
46 velocity.second = 0.0;
47 } else if (msg.type == Message::Type::jump)
48 {
49 playSound("res/Randomize87.wav", 0.25);
50
51 velocity.second = jump_velocity;
52 accel.second = jump_gravity;
53 } else if (msg.type == Message::Type::stopJump)
54 {
55 accel.second = jump_gravity_short;
56 } else if (msg.type == Message::Type::canDrop)
57 {
58 canDrop = true;
59 } else if (msg.type == Message::Type::cantDrop)
60 {
61 canDrop = false;
62 } else if (msg.type == Message::Type::drop)
63 {
64 if (canDrop)
65 {
66 canDrop = false;
67 } else {
68 entity.position.second = msg.dropAxis - entity.size.second;
69 velocity.second = 0;
70 }
71 } else if (msg.type == Message::Type::die)
72 {
73 frozen = true;
74 } else if (msg.type == Message::Type::stopDying)
75 {
76 frozen = false;
77 }
78}
79
80void PlayerPhysicsComponent::tick(Game& game, Entity& entity, double dt)
81{
82 // If frozen, do nothing
83 if (frozen)
84 {
85 return;
86 }
87
88 // Continue walking even if blocked earlier
89 if (velocity.first == 0)
90 {
91 if (direction < 0)
92 {
93 velocity.first = -90;
94 } else if (direction > 0)
95 {
96 velocity.first = 90;
97 }
98 }
99
100 // Increase gravity at the height of jump
101 if ((accel.second == jump_gravity) && (velocity.second >= 0))
102 {
103 accel.second = jump_gravity_short;
104 }
105
106 // Do the movement
107 std::pair<double, double> old_position = entity.position;
108 PhysicsBodyComponent::tick(game, entity, dt);
109
110 // Check for collisions
111 game.detectCollision(entity, old_position);
112
113 // Are we moving due to gravity?
114 if (velocity.second != 0.0)
115 {
116 isFalling = true;
117 }
118}
diff --git a/src/components/player_physics.h b/src/components/player_physics.h deleted file mode 100644 index 26f1fae..0000000 --- a/src/components/player_physics.h +++ /dev/null
@@ -1,25 +0,0 @@
1#ifndef PLAYER_PHYSICS_H
2#define PLAYER_PHYSICS_H
3
4#include "entity.h"
5#include "physics_body.h"
6
7class Game;
8
9class PlayerPhysicsComponent : public PhysicsBodyComponent {
10 public:
11 PlayerPhysicsComponent();
12 void tick(Game& game, Entity& entity, double dt);
13 void receive(Game& game, Entity& entity, const Message& msg);
14
15 private:
16 double jump_velocity;
17 double jump_gravity;
18 double jump_gravity_short;
19 int direction = 0;
20 bool canDrop = false;
21 bool frozen = false;
22 bool isFalling = false;
23};
24
25#endif
diff --git a/src/components/player_sprite.cpp b/src/components/player_sprite.cpp deleted file mode 100644 index 452a940..0000000 --- a/src/components/player_sprite.cpp +++ /dev/null
@@ -1,60 +0,0 @@
1#include "player_sprite.h"
2
3PlayerSpriteComponent::PlayerSpriteComponent() : sprite("res/Starla.png")
4{
5
6}
7
8void PlayerSpriteComponent::render(Game&, Entity& entity, Texture& buffer)
9{
10 animFrame++;
11
12 int frame = 0;
13 if (isMoving)
14 {
15 frame += 2;
16
17 if (animFrame % 20 < 10)
18 {
19 frame += 2;
20 }
21 }
22
23 if (facingLeft)
24 {
25 frame++;
26 }
27
28 double alpha = 1.0;
29 if (dying && (animFrame % 4 < 2))
30 {
31 alpha = 0.0;
32 }
33
34 Rectangle src_rect {frame*10, 0, 10, 12};
35 Rectangle dst_rect {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second};
36 buffer.blit(sprite, src_rect, dst_rect, alpha);
37}
38
39void PlayerSpriteComponent::receive(Game&, Entity&, const Message& msg)
40{
41 if (msg.type == Message::Type::walkLeft)
42 {
43 facingLeft = true;
44 isMoving = true;
45 } else if (msg.type == Message::Type::walkRight)
46 {
47 facingLeft = false;
48 isMoving = true;
49 } else if (msg.type == Message::Type::stopWalking)
50 {
51 isMoving = false;
52 } else if (msg.type == Message::Type::die)
53 {
54 dying = true;
55 isMoving = false;
56 } else if (msg.type == Message::Type::stopDying)
57 {
58 dying = false;
59 }
60}
diff --git a/src/components/player_sprite.h b/src/components/player_sprite.h deleted file mode 100644 index b1ac0af..0000000 --- a/src/components/player_sprite.h +++ /dev/null
@@ -1,23 +0,0 @@
1#ifndef PLAYER_SPRITE_H
2#define PLAYER_SPRITE_H
3
4#include "entity.h"
5#include "renderer.h"
6
7class Game;
8
9class PlayerSpriteComponent : public Component {
10 public:
11 PlayerSpriteComponent();
12 void render(Game& game, Entity& entity, Texture& buffer);
13 void receive(Game& game, Entity& entity, const Message& msg);
14
15 private:
16 Texture sprite;
17 int animFrame = 0;
18 bool facingLeft = false;
19 bool isMoving = false;
20 bool dying = false;
21};
22
23#endif
diff --git a/src/components/ponderable.h b/src/components/ponderable.h new file mode 100644 index 0000000..221d267 --- /dev/null +++ b/src/components/ponderable.h
@@ -0,0 +1,131 @@
1#ifndef TANGIBLE_H_746DB3EE
2#define TANGIBLE_H_746DB3EE
3
4#include <set>
5#include "component.h"
6#include "entity_manager.h"
7#include "vector.h"
8#include "direction.h"
9
10class PonderableComponent : public Component {
11public:
12
13 using id_type = EntityManager::id_type;
14
15 /**
16 * List of different types of physical bodies.
17 *
18 * vacuumed - Default.
19 * freefalling - The body will be treated as if there were a downward force
20 * of gravity being exerted onto it. The body will also exhibit
21 * terminal velocity (that is, its downward velocity will be
22 * capped at a constant value).
23 */
24 enum class Type {
25 vacuumed,
26 freefalling
27 };
28
29 /**
30 * List of different types of collidable surfaces.
31 */
32 enum class Collision {
33 wall,
34 platform,
35 adjacency,
36 warp,
37 danger,
38 event
39 };
40
41 /**
42 * Constructor for initializing the body type, which is a constant.
43 */
44 PonderableComponent(Type type) : type(type)
45 {
46 }
47
48 /**
49 * The velocity of the body.
50 */
51 vec2d vel = { 0.0, 0.0 };
52
53 /**
54 * The acceleration of the body.
55 */
56 vec2d accel = { 0.0, 0.0 };
57
58 /**
59 * The type of physical body that the entity is meant to assume. The body will
60 * be acted upon differently based on this. See the enumeration above for more
61 * details.
62 *
63 * @managed_by PonderingSystem
64 */
65 const Type type;
66
67 /**
68 * Whether or not a freefalling body is in contact with the ground.
69 *
70 * @managed_by PonderingSystem
71 */
72 bool grounded = false;
73
74 /**
75 * Whether or not a freefalling body is being ferried by another body.
76 *
77 * @managed_by PonderingSystem
78 */
79 bool ferried = false;
80
81 /**
82 * The entity that is ferrying this body, if there is one.
83 *
84 * @managed_by PonderingSystem
85 */
86 id_type ferry;
87
88 /**
89 * The side of the ferry that the body is resting on, if there is one.
90 *
91 * @managed_by PonderingSystem
92 */
93 Direction ferrySide;
94
95 /**
96 * The bodies that are being ferried by this body.
97 *
98 * @managed_by PonderingSystem
99 */
100 std::set<id_type> passengers;
101
102 /**
103 * If enabled, this will prevent the body from moving and accelerating. The
104 * velocity and position of the body can still be affected by sources external
105 * to the PonderingSystem. Enabling this will cause applicable bodies to
106 * become ungrounded and unferried.
107 */
108 bool frozen = false;
109
110 /**
111 * If disabled, collision detection for this body will not be performed and
112 * other bodies will ignore it. Disabling this will cause applicable bodies to
113 * become ungrounded and unferried.
114 */
115 bool collidable = true;
116
117 /**
118 * The effect that colliding with this body has.
119 */
120 Collision colliderType = Collision::wall;
121
122 /**
123 * If this flag is disabled, the entity will be ignored by the pondering
124 * system.
125 *
126 * @managed_by RealizingSystem
127 */
128 bool active = false;
129};
130
131#endif /* end of include guard: TANGIBLE_H_746DB3EE */
diff --git a/src/components/prototypable.h b/src/components/prototypable.h new file mode 100644 index 0000000..c0dd972 --- /dev/null +++ b/src/components/prototypable.h
@@ -0,0 +1,23 @@
1#ifndef PROTOTYPABLE_H_817F2205
2#define PROTOTYPABLE_H_817F2205
3
4#include "component.h"
5#include "entity_manager.h"
6
7class PrototypableComponent : public Component {
8public:
9
10 using id_type = EntityManager::id_type;
11
12 /**
13 * The index of the object in the map definition.
14 */
15 size_t mapObjectIndex;
16
17 /**
18 * The name of the prototype that the object was spawned from.
19 */
20 std::string prototypeId;
21};
22
23#endif /* end of include guard: PROTOTYPABLE_H_817F2205 */
diff --git a/src/components/runnable.h b/src/components/runnable.h new file mode 100644 index 0000000..956bfdc --- /dev/null +++ b/src/components/runnable.h
@@ -0,0 +1,51 @@
1#ifndef AUTOMATABLE_H_3D519131
2#define AUTOMATABLE_H_3D519131
3
4#include "component.h"
5#include <sol.hpp>
6#include <memory>
7#include "entity_manager.h"
8
9class RunnableComponent : public Component {
10public:
11
12 using id_type = EntityManager::id_type;
13
14 /**
15 * A Lua stack where the entity's script is running.
16 *
17 * NOTE: This object is called a thread, but there is no multi-threading going
18 * on.
19 *
20 * @managed_by ScriptingSystem
21 */
22 std::unique_ptr<sol::thread> runner;
23
24 /**
25 * An entry point to the script running in the runner thread.
26 *
27 * @managed_by ScriptingSystem
28 */
29 std::unique_ptr<sol::coroutine> callable;
30
31 /**
32 * Whether or not this entity represents a behavior script. A behavior script
33 * usually does not terminate on its own, and can be terminated at will by
34 * another system, usually when the automatable entity leaves the active map.
35 *
36 * @managed_by ScriptingSystem
37 */
38 bool behavior = false;
39
40 /**
41 * If this is a behavior script, this is the ID of the automatable entity that
42 * the behavior belongs to. This is required so that the ScriptingSystem can
43 * notify the automatable entity if the behavior script terminates by itself,
44 * and that it shouldn't attempt to terminate it.
45 *
46 * @managed_by ScriptingSystem
47 */
48 id_type actor;
49};
50
51#endif /* end of include guard: AUTOMATABLE_H_3D519131 */
diff --git a/src/components/schedulable.h b/src/components/schedulable.h new file mode 100644 index 0000000..a92bbba --- /dev/null +++ b/src/components/schedulable.h
@@ -0,0 +1,21 @@
1#ifndef SCHEDULABLE_H_1DA3FA2A
2#define SCHEDULABLE_H_1DA3FA2A
3
4#include "component.h"
5#include <tuple>
6#include <list>
7#include <functional>
8#include "entity_manager.h"
9
10class SchedulableComponent : public Component {
11public:
12
13 using id_type = EntityManager::id_type;
14
15 using Callback = std::function<void(id_type)>;
16 using Action = std::tuple<double, Callback>;
17
18 std::list<Action> actions;
19};
20
21#endif /* end of include guard: SCHEDULABLE_H_1DA3FA2A */
diff --git a/src/components/simple_collider.cpp b/src/components/simple_collider.cpp deleted file mode 100644 index f4b414e..0000000 --- a/src/components/simple_collider.cpp +++ /dev/null
@@ -1,14 +0,0 @@
1#include "simple_collider.h"
2
3SimpleColliderComponent::SimpleColliderComponent(std::function<void (Game& game, Entity& collider)> callback) : callback(callback)
4{
5
6}
7
8void SimpleColliderComponent::receive(Game& game, Entity&, const Message& msg)
9{
10 if (msg.type == Message::Type::collision)
11 {
12 callback(game, *msg.collisionEntity);
13 }
14}
diff --git a/src/components/simple_collider.h b/src/components/simple_collider.h deleted file mode 100644 index 15d78cf..0000000 --- a/src/components/simple_collider.h +++ /dev/null
@@ -1,18 +0,0 @@
1#ifndef SIMPLE_COLLIDER_H
2#define SIMPLE_COLLIDER_H
3
4#include "entity.h"
5#include <functional>
6
7class Game;
8
9class SimpleColliderComponent : public Component {
10 public:
11 SimpleColliderComponent(std::function<void (Game& game, Entity& collider)> callback);
12 void receive(Game& game, Entity& entity, const Message& msg);
13
14 private:
15 std::function<void (Game& game, Entity& collider)> callback;
16};
17
18#endif
diff --git a/src/components/static_image.cpp b/src/components/static_image.cpp deleted file mode 100644 index 9fa8dca..0000000 --- a/src/components/static_image.cpp +++ /dev/null
@@ -1,11 +0,0 @@
1#include "static_image.h"
2
3StaticImageComponent::StaticImageComponent(const char* filename) : sprite(Texture(filename))
4{
5
6}
7
8void StaticImageComponent::render(Game&, Entity& entity, Texture& buffer)
9{
10 buffer.blit(sprite, sprite.entirety(), {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second});
11}
diff --git a/src/components/static_image.h b/src/components/static_image.h deleted file mode 100644 index 2dec78b..0000000 --- a/src/components/static_image.h +++ /dev/null
@@ -1,18 +0,0 @@
1#ifndef STATIC_IMAGE_H
2#define STATIC_IMAGE_H
3
4#include "entity.h"
5#include "renderer.h"
6
7class Game;
8
9class StaticImageComponent : public Component {
10 public:
11 StaticImageComponent(const char* filename);
12 void render(Game& game, Entity& entity, Texture& buffer);
13
14 private:
15 Texture sprite;
16};
17
18#endif
diff --git a/src/components/transformable.h b/src/components/transformable.h new file mode 100644 index 0000000..bb21996 --- /dev/null +++ b/src/components/transformable.h
@@ -0,0 +1,35 @@
1#ifndef LOCATABLE_H_39E526CA
2#define LOCATABLE_H_39E526CA
3
4#include "component.h"
5#include "vector.h"
6
7class TransformableComponent : public Component {
8public:
9
10 /**
11 * The coordinates of the entity.
12 *
13 * Note that ponderable entities sometimes have special behavior related to
14 * their coordinates, specifically that ferried bodies will behave oddly if
15 * their coordinates are changed outside of the PonderingSystem. Before doing
16 * so, use PonderingSystem::unferry on the body to ensure that it is not
17 * ferried.
18 */
19 vec2d pos;
20
21 /**
22 * The size of the entity.
23 */
24 vec2i size;
25
26 /**
27 * For prototypes, the original coordinates and size of the entity.
28 *
29 * @managed_by RealizingSystem
30 */
31 vec2d origPos;
32 vec2i origSize;
33};
34
35#endif /* end of include guard: LOCATABLE_H_39E526CA */
diff --git a/src/components/user_movement.cpp b/src/components/user_movement.cpp deleted file mode 100644 index e499fee..0000000 --- a/src/components/user_movement.cpp +++ /dev/null
@@ -1,100 +0,0 @@
1#include "user_movement.h"
2#include "renderer.h"
3
4void UserMovementComponent::input(Game& game, Entity& entity, int key, int action)
5{
6 if (action == GLFW_PRESS)
7 {
8 if (key == GLFW_KEY_LEFT)
9 {
10 holdingLeft = true;
11
12 if (!frozen)
13 {
14 entity.send(game, Message::Type::walkLeft);
15 }
16 } else if (key == GLFW_KEY_RIGHT)
17 {
18 holdingRight = true;
19
20 if (!frozen)
21 {
22 entity.send(game, Message::Type::walkRight);
23 }
24 } else if (key == GLFW_KEY_UP)
25 {
26 if (!frozen)
27 {
28 entity.send(game, Message::Type::jump);
29 }
30 } else if (key == GLFW_KEY_DOWN)
31 {
32 if (!frozen)
33 {
34 entity.send(game, Message::Type::canDrop);
35 }
36 }
37 } else if (action == GLFW_RELEASE)
38 {
39 if (key == GLFW_KEY_LEFT)
40 {
41 holdingLeft = false;
42
43 if (!frozen)
44 {
45 if (holdingRight)
46 {
47 entity.send(game, Message::Type::walkRight);
48 } else {
49 entity.send(game, Message::Type::stopWalking);
50 }
51 }
52 } else if (key == GLFW_KEY_RIGHT)
53 {
54 holdingRight = false;
55
56 if (!frozen)
57 {
58 if (holdingLeft)
59 {
60 entity.send(game, Message::Type::walkLeft);
61 } else {
62 entity.send(game, Message::Type::stopWalking);
63 }
64 }
65 } else if (key == GLFW_KEY_DOWN)
66 {
67 if (!frozen)
68 {
69 entity.send(game, Message::Type::cantDrop);
70 }
71 } else if (key == GLFW_KEY_UP)
72 {
73 if (!frozen)
74 {
75 entity.send(game, Message::Type::stopJump);
76 }
77 }
78 }
79}
80
81void UserMovementComponent::receive(Game& game, Entity& entity, const Message& msg)
82{
83 if (msg.type == Message::Type::die)
84 {
85 frozen = true;
86
87 entity.send(game, Message::Type::stopWalking);
88 } else if (msg.type == Message::Type::stopDying)
89 {
90 frozen = false;
91
92 if (holdingLeft)
93 {
94 entity.send(game, Message::Type::walkLeft);
95 } else if (holdingRight)
96 {
97 entity.send(game, Message::Type::walkRight);
98 }
99 }
100}
diff --git a/src/components/user_movement.h b/src/components/user_movement.h deleted file mode 100644 index 1bcf05e..0000000 --- a/src/components/user_movement.h +++ /dev/null
@@ -1,19 +0,0 @@
1#ifndef USER_MOVEMENT_H
2#define USER_MOVEMENT_H
3
4#include "entity.h"
5
6class Game;
7
8class UserMovementComponent : public Component {
9 public:
10 void input(Game& game, Entity& entity, int key, int action);
11 void receive(Game&, Entity&, const Message& msg);
12
13 private:
14 bool holdingLeft = false;
15 bool holdingRight = false;
16 bool frozen = false;
17};
18
19#endif
diff --git a/src/consts.h b/src/consts.h index 804c761..f3f777f 100644 --- a/src/consts.h +++ b/src/consts.h
@@ -7,8 +7,21 @@ const int GAME_WIDTH = 320;
7const int GAME_HEIGHT = 200; 7const int GAME_HEIGHT = 200;
8const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; 8const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH;
9const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1; 9const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1;
10const int WALL_GAP = 5;
11const int TILESET_COLS = 8;
12const int FONT_COLS = 16;
10 13
11const int FRAMES_PER_SECOND = 60; 14const int FRAMES_PER_SECOND = 60;
12const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND; 15const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND;
13 16
17#define CALC_VELOCITY(h, l) (-2 * (h) / (l))
18#define CALC_GRAVITY(h, l) (2 * ((h) / (l)) / (l))
19
20const double NORMAL_GRAVITY = CALC_GRAVITY(TILE_HEIGHT*3.5, 0.233);
21const double JUMP_GRAVITY = CALC_GRAVITY(TILE_HEIGHT*4.5, 0.3);
22const double JUMP_VELOCITY = CALC_VELOCITY(TILE_HEIGHT*4.5, 0.3);
23
24const double WALK_SPEED = 90;
25const double TERMINAL_VELOCITY = 240;
26
14#endif 27#endif
diff --git a/src/direction.h b/src/direction.h new file mode 100644 index 0000000..9a4c801 --- /dev/null +++ b/src/direction.h
@@ -0,0 +1,11 @@
1#ifndef DIRECTION_H_9C49EAFD
2#define DIRECTION_H_9C49EAFD
3
4enum class Direction {
5 left,
6 right,
7 up,
8 down
9};
10
11#endif /* end of include guard: DIRECTION_H_9C49EAFD */
diff --git a/src/entity.cpp b/src/entity.cpp deleted file mode 100644 index 2b6cd7f..0000000 --- a/src/entity.cpp +++ /dev/null
@@ -1,46 +0,0 @@
1#include "entity.h"
2
3void Entity::addComponent(std::shared_ptr<Component> c)
4{
5 components.push_back(c);
6}
7
8void Entity::send(Game& game, const Message& msg)
9{
10 for (auto component : components)
11 {
12 component->receive(game, *this, msg);
13 }
14}
15
16void Entity::tick(Game& game, double dt)
17{
18 for (auto component : components)
19 {
20 component->tick(game, *this, dt);
21 }
22}
23
24void Entity::input(Game& game, int key, int action)
25{
26 for (auto component : components)
27 {
28 component->input(game, *this, key, action);
29 }
30}
31
32void Entity::render(Game& game, Texture& buffer)
33{
34 for (auto component : components)
35 {
36 component->render(game, *this, buffer);
37 }
38}
39
40void Entity::detectCollision(Game& game, Entity& collider, std::pair<double, double> old_position)
41{
42 for (auto component : components)
43 {
44 component->detectCollision(game, *this, collider, old_position);
45 }
46}
diff --git a/src/entity.h b/src/entity.h deleted file mode 100644 index 7f09f2d..0000000 --- a/src/entity.h +++ /dev/null
@@ -1,64 +0,0 @@
1#ifndef ENTITY_H
2#define ENTITY_H
3
4#include <list>
5#include "renderer.h"
6
7class Game;
8class Map;
9class Entity;
10class Component;
11
12class Message {
13 public:
14 enum class Type {
15 walkLeft,
16 walkRight,
17 stopWalking,
18 setHorizontalVelocity,
19 setVerticalVelocity,
20 collision,
21 jump,
22 stopJump,
23 drop,
24 canDrop,
25 cantDrop,
26 die,
27 stopDying,
28 hitTheGround
29 };
30
31 Message(Type type) : type(type) {}
32
33 Type type;
34 Entity* collisionEntity;
35 int dropAxis;
36 double velocity;
37};
38
39class Entity {
40 public:
41 void addComponent(std::shared_ptr<Component> c);
42 void send(Game& game, const Message& msg);
43 void tick(Game& game, double dt);
44 void input(Game& game, int key, int action);
45 void render(Game& game, Texture& buffer);
46 void detectCollision(Game& game, Entity& collider, std::pair<double, double> old_position);
47
48 std::pair<double, double> position;
49 std::pair<int, int> size;
50
51 private:
52 std::list<std::shared_ptr<Component>> components;
53};
54
55class Component {
56 public:
57 virtual void receive(Game&, Entity&, const Message&) {}
58 virtual void render(Game&, Entity&, Texture&) {}
59 virtual void tick(Game&, Entity&, double) {}
60 virtual void input(Game&, Entity&, int, int) {}
61 virtual void detectCollision(Game&, Entity&, Entity&, std::pair<double, double>) {}
62};
63
64#endif
diff --git a/src/entity_manager.cpp b/src/entity_manager.cpp new file mode 100644 index 0000000..0aaaf8e --- /dev/null +++ b/src/entity_manager.cpp
@@ -0,0 +1,39 @@
1#ifndef ENTITY_MANAGER_CPP_42D78C22
2#define ENTITY_MANAGER_CPP_42D78C22
3
4#include "entity_manager.h"
5
6template <>
7std::set<EntityManager::id_type> EntityManager::getEntitiesWithComponents<>(
8 std::set<std::type_index>& componentTypes) const
9{
10 if (cachedComponents.count(componentTypes) == 1)
11 {
12 return cachedComponents[componentTypes];
13 }
14
15 std::set<id_type>& cache = cachedComponents[componentTypes];
16 for (id_type entity = 0; entity < entities.size(); entity++)
17 {
18 const EntityData& data = entities[entity];
19 bool cacheEntity = true;
20
21 for (auto& componentType : componentTypes)
22 {
23 if (data.components.count(componentType) == 0)
24 {
25 cacheEntity = false;
26 break;
27 }
28 }
29
30 if (cacheEntity)
31 {
32 cache.insert(entity);
33 }
34 }
35
36 return cache;
37}
38
39#endif /* end of include guard: ENTITY_MANAGER_CPP_42D78C22 */
diff --git a/src/entity_manager.h b/src/entity_manager.h new file mode 100644 index 0000000..b2ef0de --- /dev/null +++ b/src/entity_manager.h
@@ -0,0 +1,230 @@
1#ifndef ENTITY_MANAGER_H_C5832F11
2#define ENTITY_MANAGER_H_C5832F11
3
4#include <map>
5#include <vector>
6#include <typeindex>
7#include <set>
8#include <stdexcept>
9#include "component.h"
10#include "util.h"
11
12class EntityManager {
13private:
14
15 struct EntityData {
16 std::map<std::type_index, std::unique_ptr<Component>> components;
17 };
18
19 using database_type = std::vector<EntityData>;
20
21public:
22
23 using id_type = database_type::size_type;
24
25private:
26
27 database_type entities;
28 std::vector<bool> slotAvailable;
29 std::set<id_type> allEntities;
30
31 mutable std::map<std::set<std::type_index>, std::set<id_type>>
32 cachedComponents;
33
34 id_type nextEntityID = 0;
35
36 template <class T, class... R>
37 std::set<id_type> getEntitiesWithComponentsHelper(
38 std::set<std::type_index>& componentTypes) const
39 {
40 componentTypes.insert(typeid(T));
41
42 return getEntitiesWithComponents<R...>(componentTypes);
43 }
44
45 template <class... R>
46 std::set<id_type> getEntitiesWithComponents(
47 std::set<std::type_index>& componentTypes) const
48 {
49 return getEntitiesWithComponentsHelper<R...>(componentTypes);
50 }
51
52public:
53
54 EntityManager() = default;
55
56 EntityManager(const EntityManager& copy) = delete;
57
58 id_type emplaceEntity()
59 {
60 if (nextEntityID >= entities.size())
61 {
62 // If the database is saturated, add a new element for the new entity.
63 entities.emplace_back();
64 slotAvailable.push_back(false);
65 allEntities.insert(nextEntityID);
66
67 return nextEntityID++;
68 } else {
69 // If there is an available slot in the database, use it.
70 id_type id = nextEntityID++;
71 slotAvailable[id] = false;
72 allEntities.insert(id);
73
74 // Fast forward the next available slot pointer to an available slot.
75 while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID])
76 {
77 nextEntityID++;
78 }
79
80 return id;
81 }
82 }
83
84 void deleteEntity(id_type entity)
85 {
86 if ((entity >= entities.size()) || slotAvailable[entity])
87 {
88 throw std::invalid_argument("Cannot delete non-existent entity");
89 }
90
91 // Uncache components
92 for (auto& cache : cachedComponents)
93 {
94 cache.second.erase(entity);
95 }
96
97 allEntities.erase(entity);
98
99 // Destroy the data
100 entities[entity].components.clear();
101
102 // Mark the slot as available
103 slotAvailable[entity] = true;
104
105 if (entity < nextEntityID)
106 {
107 nextEntityID = entity;
108 }
109 }
110
111 template <class T, class... Args>
112 T& emplaceComponent(id_type entity, Args&&... args)
113 {
114 if ((entity >= entities.size()) || slotAvailable[entity])
115 {
116 throw std::invalid_argument("Cannot get non-existent entity");
117 }
118
119 EntityData& data = entities[entity];
120 std::type_index componentType = typeid(T);
121
122 if (data.components.count(componentType))
123 {
124 throw std::invalid_argument("Cannot emplace already-existent component");
125 }
126
127 // Initialize the component
128 std::unique_ptr<T> ptr(new T(std::forward<Args>(args)...));
129 T& component = *ptr;
130 data.components[componentType] = std::move(ptr);
131
132 // Invalidate related caches
133 erase_if(
134 cachedComponents,
135 [&componentType] (
136 std::pair<const std::set<std::type_index>, std::set<id_type>>& cache) {
137 return cache.first.count(componentType) == 1;
138 });
139
140 return component;
141 }
142
143 template <class T>
144 void removeComponent(id_type entity)
145 {
146 if ((entity >= entities.size()) || slotAvailable[entity])
147 {
148 throw std::invalid_argument("Cannot get non-existent entity");
149 }
150
151 EntityData& data = entities[entity];
152 std::type_index componentType = typeid(T);
153
154 if (!data.components.count(componentType))
155 {
156 throw std::invalid_argument("Cannot delete non-existent component");
157 }
158
159 // Destroy the component
160 data.components.erase(componentType);
161
162 // Uncache the component
163 for (auto& cache : cachedComponents)
164 {
165 if (cache.first.count(componentType) == 1)
166 {
167 cache.second.erase(entity);
168 }
169 }
170 }
171
172 template <class T>
173 const T& getComponent(id_type entity) const
174 {
175 if ((entity >= entities.size()) || slotAvailable[entity])
176 {
177 throw std::invalid_argument("Cannot get non-existent entity");
178 }
179
180 const EntityData& data = entities[entity];
181 std::type_index componentType = typeid(T);
182
183 if (!data.components.count(componentType))
184 {
185 throw std::invalid_argument("Cannot get non-existent component");
186 }
187
188 return *dynamic_cast<const T*>(data.components.at(componentType).get());
189 }
190
191 template <class T>
192 T& getComponent(id_type entity)
193 {
194 return const_cast<T&>(
195 static_cast<const EntityManager&>(*this).getComponent<T>(entity));
196 }
197
198 template <class T>
199 bool hasComponent(id_type entity) const
200 {
201 if ((entity >= entities.size()) || slotAvailable[entity])
202 {
203 throw std::invalid_argument("Cannot get non-existent entity");
204 }
205
206 const EntityData& data = entities[entity];
207 std::type_index componentType = typeid(T);
208
209 return data.components.count(componentType);
210 }
211
212 template <class... R>
213 std::set<id_type> getEntitiesWithComponents() const
214 {
215 std::set<std::type_index> componentTypes;
216
217 return getEntitiesWithComponentsHelper<R...>(componentTypes);
218 }
219
220 const std::set<id_type>& getEntities() const
221 {
222 return allEntities;
223 }
224};
225
226template <>
227std::set<EntityManager::id_type> EntityManager::getEntitiesWithComponents<>(
228 std::set<std::type_index>& componentTypes) const;
229
230#endif /* end of include guard: ENTITY_MANAGER_H_C5832F11 */
diff --git a/src/entityfactory.cpp b/src/entityfactory.cpp deleted file mode 100644 index b80fe99..0000000 --- a/src/entityfactory.cpp +++ /dev/null
@@ -1,182 +0,0 @@
1#include "entityfactory.h"
2#include <libxml/parser.h>
3#include "muxer.h"
4#include <cstdio>
5#include <map>
6#include <list>
7#include "components/static_image.h"
8#include "components/simple_collider.h"
9#include "components/physics_body.h"
10#include "components/ai.h"
11#include "game.h"
12
13void parseEntityAIData(AI& ai, xmlNodePtr node, const std::map<std::string, int>& items)
14{
15 xmlChar* key;
16
17 for (xmlNodePtr aiNode = node->xmlChildrenNode; aiNode != NULL; aiNode = aiNode->next)
18 {
19 if (!xmlStrcmp(aiNode->name, (xmlChar*) "move"))
20 {
21 MoveAIAction::Direction dir;
22 int len;
23 int speed;
24
25 key = xmlGetProp(aiNode, (xmlChar*) "direction");
26 if (key == 0) exit(2);
27 if (!xmlStrcmp(key, (xmlChar*) "left"))
28 {
29 dir = MoveAIAction::Direction::Left;
30 } else if (!xmlStrcmp(key, (xmlChar*) "right"))
31 {
32 dir = MoveAIAction::Direction::Right;
33 } else if (!xmlStrcmp(key, (xmlChar*) "up"))
34 {
35 dir = MoveAIAction::Direction::Up;
36 } else if (!xmlStrcmp(key, (xmlChar*) "down"))
37 {
38 dir = MoveAIAction::Direction::Down;
39 } else {
40 exit(2);
41 }
42 xmlFree(key);
43
44 key = xmlGetProp(aiNode, (xmlChar*) "length");
45 if (key != 0)
46 {
47 len = atoi((char*) key);
48 } else {
49 key = xmlGetProp(aiNode, (xmlChar*) "length-var");
50 if (key == 0) exit(2);
51 std::string varName = (char*) key;
52 len = items.at(varName);
53 }
54 xmlFree(key);
55
56 key = xmlGetProp(aiNode, (xmlChar*) "speed");
57 if (key != 0)
58 {
59 speed = atoi((char*) key);
60 } else {
61 key = xmlGetProp(aiNode, (xmlChar*) "speed-var");
62 if (key == 0) exit(2);
63 std::string varName = (char*) key;
64 speed = items.at(varName);
65 }
66 xmlFree(key);
67
68 ai.addAction(std::make_shared<MoveAIAction>(dir, len, speed));
69 } else if (!xmlStrcmp(aiNode->name, (xmlChar*) "switch"))
70 {
71 key = xmlGetProp(aiNode, (xmlChar*) "item");
72 if (key == 0) exit(2);
73 std::string switchItem = (char*) key;
74 xmlFree(key);
75
76 for (xmlNodePtr switchNode = aiNode->xmlChildrenNode; switchNode != NULL; switchNode = switchNode->next)
77 {
78 if (!xmlStrcmp(switchNode->name, (xmlChar*) "case"))
79 {
80 key = xmlGetProp(switchNode, (xmlChar*) "value");
81 if (key == 0) exit(2);
82 int caseValue = atoi((char*) key);
83 xmlFree(key);
84
85 if (items.at(switchItem) == caseValue)
86 {
87 parseEntityAIData(ai, switchNode, items);
88 }
89 }
90 }
91 }
92 }
93}
94
95std::shared_ptr<Entity> EntityFactory::createNamedEntity(const std::string name, const std::map<std::string, int>& items)
96{
97 xmlDocPtr doc = xmlParseFile("res/entities.xml");
98 if (doc == nullptr)
99 {
100 fprintf(stderr, "Error reading entities\n");
101 exit(-1);
102 }
103
104 xmlNodePtr top = xmlDocGetRootElement(doc);
105 if (top == nullptr)
106 {
107 fprintf(stderr, "Empty entities file\n");
108 exit(-1);
109 }
110
111 if (xmlStrcmp(top->name, (const xmlChar*) "entities"))
112 {
113 fprintf(stderr, "Invalid entities definition\n");
114 exit(-1);
115 }
116
117 auto entity = std::make_shared<Entity>();
118
119 xmlChar* key;
120 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
121 {
122 if (!xmlStrcmp(node->name, (xmlChar*) "entity"))
123 {
124 key = xmlGetProp(node, (xmlChar*) "id");
125 if (key == 0) exit(-1);
126 std::string entityID = (char*) key;
127 xmlFree(key);
128
129 if (entityID == name)
130 {
131 key = xmlGetProp(node, (xmlChar*) "sprite");
132 if (key == 0) exit(-1);
133 auto spriteComponent = std::make_shared<StaticImageComponent>((char*) key);
134 entity->addComponent(spriteComponent);
135 xmlFree(key);
136
137 auto physicsComponent = std::make_shared<PhysicsBodyComponent>();
138 entity->addComponent(physicsComponent);
139
140 key = xmlGetProp(node, (xmlChar*) "width");
141 if (key == 0) exit(-1);
142 entity->size.first = atoi((char*) key);
143 xmlFree(key);
144
145 key = xmlGetProp(node, (xmlChar*) "height");
146 if (key == 0) exit(-1);
147 entity->size.second = atoi((char*) key);
148 xmlFree(key);
149
150 bool addAI = false;
151 auto aiComponent = std::make_shared<AIComponent>();
152
153 for (xmlNodePtr entityNode = node->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
154 {
155 if (!xmlStrcmp(entityNode->name, (xmlChar*) "ai"))
156 {
157 addAI = true;
158
159 xmlChar* chanceKey = xmlGetProp(entityNode, (xmlChar*) "chance");
160 if (chanceKey == 0) exit(2);
161 int chance = atoi((char*) chanceKey);
162 xmlFree(chanceKey);
163
164 AI& ai = aiComponent->emplaceAI(chance);
165 parseEntityAIData(ai, entityNode, items);
166 }
167 }
168
169 if (addAI)
170 {
171 entity->addComponent(aiComponent);
172 }
173
174 break;
175 }
176 }
177 }
178
179 xmlFreeDoc(doc);
180
181 return entity;
182}
diff --git a/src/entityfactory.h b/src/entityfactory.h deleted file mode 100644 index 56f7216..0000000 --- a/src/entityfactory.h +++ /dev/null
@@ -1,15 +0,0 @@
1#ifndef ENTITYFACTORY_H
2#define ENTITYFACTORY_H
3
4#include <string>
5#include <map>
6
7class Entity;
8class Map;
9
10class EntityFactory {
11 public:
12 static std::shared_ptr<Entity> createNamedEntity(const std::string name, const std::map<std::string, int>& items);
13};
14
15#endif
diff --git a/src/game.cpp b/src/game.cpp index 673c804..eb57bc6 100644 --- a/src/game.cpp +++ b/src/game.cpp
@@ -1,178 +1,78 @@
1#include "game.h" 1#include "game.h"
2#include <cstdlib> 2#include "systems/controlling.h"
3#include "renderer.h" 3#include "systems/pondering.h"
4#include "muxer.h" 4#include "systems/animating.h"
5#include "map.h" 5#include "systems/mapping.h"
6#include "components/user_movement.h" 6#include "systems/orienting.h"
7#include "components/player_physics.h" 7#include "systems/playing.h"
8#include "components/player_sprite.h" 8#include "systems/scheduling.h"
9#include "components/map_render.h" 9#include "systems/realizing.h"
10#include "components/map_collision.h" 10#include "systems/scripting.h"
11#include "consts.h" 11#include "consts.h"
12 12
13Game::Game(const char* mapfile) : world(mapfile)
14{
15 // Set up entities
16 player = std::make_shared<Entity>();
17 player->position = world.getStartingPosition();
18 player->size = std::make_pair(10.0,12.0);
19
20 auto player_input = std::make_shared<UserMovementComponent>();
21 player->addComponent(player_input);
22
23 auto player_physics = std::make_shared<PlayerPhysicsComponent>();
24 player->addComponent(player_physics);
25
26 auto player_anim = std::make_shared<PlayerSpriteComponent>();
27 player->addComponent(player_anim);
28
29 const Map& startingMap = world.getStartingMap();
30 save = {&startingMap, player->position};
31
32 loadMap(startingMap, player->position);
33}
34
35void key_callback(GLFWwindow* window, int key, int, int action, int) 13void key_callback(GLFWwindow* window, int key, int, int action, int)
36{ 14{
37 Game* game = (Game*) glfwGetWindowUserPointer(window); 15 Game& game = *static_cast<Game*>(glfwGetWindowUserPointer(window));
38 16
39 if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS)) 17 if ((action == GLFW_PRESS) && (key == GLFW_KEY_ESCAPE))
40 {
41 game->shouldQuit = true;
42 }
43
44 for (auto entity : game->entities)
45 { 18 {
46 entity->input(*game, key, action); 19 game.shouldQuit_ = true;
20
21 return;
47 } 22 }
23
24 game.systemManager_.input(key, action);
48} 25}
49 26
50void Game::execute(GLFWwindow* window) 27Game::Game(std::mt19937& rng) : rng_(rng)
51{ 28{
29 systemManager_.emplaceSystem<PlayingSystem>(*this);
30 systemManager_.emplaceSystem<SchedulingSystem>(*this);
31 systemManager_.emplaceSystem<ControllingSystem>(*this);
32 systemManager_.emplaceSystem<ScriptingSystem>(*this);
33 systemManager_.emplaceSystem<OrientingSystem>(*this);
34 systemManager_.emplaceSystem<PonderingSystem>(*this);
35 systemManager_.emplaceSystem<MappingSystem>(*this);
36 systemManager_.emplaceSystem<AnimatingSystem>(*this);
37
38 systemManager_.emplaceSystem<RealizingSystem>(*this,
39 "res/maps.xml",
40 "res/entities.xml");
41
42 systemManager_.getSystem<PlayingSystem>().initPlayer();
43
52 glfwSwapInterval(1); 44 glfwSwapInterval(1);
53 glfwSetWindowUserPointer(window, this); 45 glfwSetWindowUserPointer(renderer_.getWindow().getHandle(), this);
54 glfwSetKeyCallback(window, key_callback); 46 glfwSetKeyCallback(renderer_.getWindow().getHandle(), key_callback);
55 47}
56 Texture buffer(GAME_WIDTH, GAME_HEIGHT);
57 48
49void Game::execute()
50{
58 double lastTime = glfwGetTime(); 51 double lastTime = glfwGetTime();
59 const double dt = 0.01; 52 const double dt = 0.01;
60 double accumulator = 0.0; 53 double accumulator = 0.0;
61 54 Texture texture(GAME_WIDTH, GAME_HEIGHT);
62 while (!(shouldQuit || glfwWindowShouldClose(window))) 55
56 while (!(shouldQuit_ ||
57 glfwWindowShouldClose(renderer_.getWindow().getHandle())))
63 { 58 {
64 double currentTime = glfwGetTime(); 59 double currentTime = glfwGetTime();
65 double frameTime = currentTime - lastTime; 60 double frameTime = currentTime - lastTime;
66 lastTime = currentTime; 61 lastTime = currentTime;
67 62
68 // Should we load a new world?
69 if (newWorld)
70 {
71 newWorld = false;
72 entities.clear();
73 entities = std::move(nextEntities);
74
75 player->position = nextPosition;
76 }
77
78 // Handle input
79 glfwPollEvents(); 63 glfwPollEvents();
80 64
81 // Tick!
82 accumulator += frameTime; 65 accumulator += frameTime;
83 while (accumulator >= dt) 66 while (accumulator >= dt)
84 { 67 {
85 for (auto entity : entities) 68 systemManager_.tick(dt);
86 { 69
87 entity->tick(*this, dt);
88 }
89
90 accumulator -= dt; 70 accumulator -= dt;
91 } 71 }
92
93 // Do any scheduled tasks
94 for (auto& task : scheduled)
95 {
96 task.first -= frameTime;
97
98 if (task.first <= 0)
99 {
100 task.second();
101 }
102 }
103
104 scheduled.remove_if([] (std::pair<double, std::function<void ()>> value) { return value.first <= 0; });
105
106 // Do rendering
107 buffer.fill(buffer.entirety(), 0, 0, 0);
108 for (auto entity : entities)
109 {
110 entity->render(*this, buffer);
111 }
112 72
113 buffer.renderScreen(); 73 // Render
74 renderer_.fill(texture, texture.entirety(), 0, 0, 0);
75 systemManager_.render(texture);
76 renderer_.renderScreen(texture);
114 } 77 }
115} 78}
116
117void Game::loadMap(const Map& map, std::pair<double, double> position)
118{
119 auto mapEn = std::make_shared<Entity>();
120
121 auto map_render = std::make_shared<MapRenderComponent>(map);
122 mapEn->addComponent(map_render);
123
124 auto map_collision = std::make_shared<MapCollisionComponent>(map);
125 mapEn->addComponent(map_collision);
126
127 // Map in the back, player on top, rest of entities in between
128 nextEntities.clear();
129 nextEntities.push_back(mapEn);
130 map.createEntities(nextEntities);
131 nextEntities.push_back(player);
132
133 newWorld = true;
134
135 currentMap = &map;
136 nextPosition = position;
137}
138
139void Game::detectCollision(Entity& collider, std::pair<double, double> old_position)
140{
141 for (auto entity : entities)
142 {
143 entity->detectCollision(*this, collider, old_position);
144 }
145}
146
147void Game::saveGame()
148{
149 save = {currentMap, player->position};
150}
151
152void Game::schedule(double time, std::function<void ()> callback)
153{
154 scheduled.emplace_front(time, std::move(callback));
155}
156
157void Game::playerDie()
158{
159 player->send(*this, Message::Type::die);
160
161 playSound("res/Hit_Hurt5.wav", 0.25);
162
163 schedule(0.75, [&] () {
164 if (*currentMap != *save.map)
165 {
166 loadMap(*save.map, save.position);
167 } else {
168 player->position = save.position;
169 }
170
171 player->send(*this, Message::Type::stopDying);
172 });
173}
174
175const World& Game::getWorld() const
176{
177 return world;
178}
diff --git a/src/game.h b/src/game.h index dd4b2f7..d7fdcd7 100644 --- a/src/game.h +++ b/src/game.h
@@ -1,45 +1,52 @@
1#ifndef GAME_H 1#ifndef GAME_H_1014DDC9
2#define GAME_H 2#define GAME_H_1014DDC9
3 3
4#include <memory> 4#include <random>
5#include <functional> 5#include "entity_manager.h"
6#include <list> 6#include "system_manager.h"
7#include <map> 7#include "renderer/renderer.h"
8#include "map.h"
9#include "world.h"
10
11class Entity;
12struct GLFWwindow;
13
14struct Savefile {
15 const Map* map;
16 std::pair<double, double> position;
17};
18 8
19class Game { 9class Game {
20 public: 10public:
21 Game(const char* maps); 11
22 void execute(GLFWwindow* window); 12 Game(std::mt19937& rng);
23 void loadMap(const Map& map, std::pair<double, double> position); 13
24 void detectCollision(Entity& collider, std::pair<double, double> old_position); 14 void execute();
25 void saveGame(); 15
26 void schedule(double time, std::function<void ()> callback); 16 inline std::mt19937& getRng()
27 void playerDie(); 17 {
28 const World& getWorld() const; 18 return rng_;
29 19 }
30 private: 20
31 friend void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); 21 inline Renderer& getRenderer()
32 22 {
33 std::list<std::shared_ptr<Entity>> entities; 23 return renderer_;
34 std::list<std::shared_ptr<Entity>> nextEntities; 24 }
35 std::pair<double, double> nextPosition; 25
36 bool newWorld; 26 inline EntityManager& getEntityManager()
37 std::shared_ptr<Entity> player; 27 {
38 const Map* currentMap; 28 return entityManager_;
39 Savefile save; 29 }
40 std::list<std::pair<double, std::function<void ()>>> scheduled; 30
41 bool shouldQuit = false; 31 inline SystemManager& getSystemManager()
42 World world; 32 {
33 return systemManager_;
34 }
35
36 friend void key_callback(
37 GLFWwindow* window,
38 int key,
39 int scancode,
40 int action,
41 int mods);
42
43private:
44
45 std::mt19937 rng_;
46 Renderer renderer_;
47 SystemManager systemManager_;
48 EntityManager entityManager_;
49 bool shouldQuit_ = false;
43}; 50};
44 51
45#endif 52#endif /* end of include guard: GAME_H_1014DDC9 */
diff --git a/src/main.cpp b/src/main.cpp index 4157350..d59d0f9 100644 --- a/src/main.cpp +++ b/src/main.cpp
@@ -1,25 +1,18 @@
1#include <ctime> 1#include <random>
2#include <list>
3#include "renderer.h"
4#include <cstdlib>
5#include "game.h"
6#include "muxer.h" 2#include "muxer.h"
3#include "game.h"
7 4
8int main() 5int main()
9{ 6{
10 srand(time(NULL)); 7 std::random_device randomDevice;
11 8 std::mt19937 rng(randomDevice());
12 GLFWwindow* window = initRenderer(); 9
13 initMuxer(); 10 initMuxer();
14 11
15 // Put this in a block so game goes out of scope before we destroy the renderer 12 Game game(rng);
16 { 13 game.execute();
17 Game game {"res/maps.xml"}; 14
18 game.execute(window);
19 }
20
21 destroyMuxer(); 15 destroyMuxer();
22 destroyRenderer(); 16
23
24 return 0; 17 return 0;
25} 18}
diff --git a/src/map.cpp b/src/map.cpp deleted file mode 100644 index e3725b2..0000000 --- a/src/map.cpp +++ /dev/null
@@ -1,151 +0,0 @@
1#include "map.h"
2#include <cstdlib>
3#include <cstring>
4#include <map>
5#include "entityfactory.h"
6#include "entity.h"
7#include "game.h"
8#include "consts.h"
9
10Map::Map(int id)
11{
12 this->id = id;
13 mapdata = (int*) calloc(1, sizeof(int));
14}
15
16Map::Map(const Map& map)
17{
18 mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
19 memcpy(mapdata, map.mapdata, MAP_WIDTH*MAP_HEIGHT*sizeof(int));
20
21 id = map.id;
22 title = map.title;
23 adjacents = map.adjacents;
24 entities = map.entities;
25}
26
27Map::Map(Map&& map) : Map()
28{
29 swap(*this, map);
30}
31
32Map::~Map()
33{
34 free(mapdata);
35}
36
37Map& Map::operator= (Map map)
38{
39 swap(*this, map);
40
41 return *this;
42}
43
44void swap(Map& first, Map& second)
45{
46 std::swap(first.mapdata, second.mapdata);
47 std::swap(first.title, second.title);
48 std::swap(first.adjacents, second.adjacents);
49 std::swap(first.id, second.id);
50 std::swap(first.entities, second.entities);
51}
52
53int Map::getID() const
54{
55 return id;
56}
57
58const int* Map::getMapdata() const
59{
60 return mapdata;
61}
62
63std::string Map::getTitle() const
64{
65 return title;
66}
67
68void Map::createEntities(std::list<std::shared_ptr<Entity>>& entities) const
69{
70 for (auto data : this->entities)
71 {
72 auto entity = EntityFactory::createNamedEntity(data.name, data.items);
73 entity->position = data.position;
74
75 entities.push_back(entity);
76 }
77}
78
79bool Map::operator==(const Map& other) const
80{
81 return id == other.id;
82}
83
84bool Map::operator!=(const Map& other) const
85{
86 return id != other.id;
87}
88
89Map::MoveType Map::moveTypeForShort(std::string str)
90{
91 if (str == "wrap") return MoveType::Wrap;
92 if (str == "warp") return MoveType::Warp;
93 if (str == "reverseWarp") return MoveType::ReverseWarp;
94
95 return MoveType::Wall;
96}
97
98Map::MoveDir Map::moveDirForShort(std::string str)
99{
100 if (str == "right") return MoveDir::Right;
101 if (str == "up") return MoveDir::Up;
102 if (str == "down") return MoveDir::Down;
103
104 return MoveDir::Left;
105}
106
107static const Map::Adjacent defaultAdjacent {};
108const Map::Adjacent& Map::getAdjacent(MoveDir dir) const
109{
110 if (adjacents.count(dir) > 0)
111 {
112 return adjacents.at(dir);
113 } else {
114 return defaultAdjacent;
115 }
116}
117
118bool Map::moveTypeTakesMap(MoveType type)
119{
120 switch (type)
121 {
122 case MoveType::Wall: return false;
123 case MoveType::Wrap: return false;
124 case MoveType::Warp: return true;
125 case MoveType::ReverseWarp: return true;
126 }
127}
128
129void Map::setMapdata(int* mapdata)
130{
131 free(this->mapdata);
132 this->mapdata = mapdata;
133}
134
135void Map::setTitle(std::string title)
136{
137 this->title = title;
138}
139
140void Map::setAdjacent(MoveDir dir, MoveType type, int map)
141{
142 Adjacent& cur = adjacents[dir];
143 cur.type = type;
144 if (map != -1) cur.map = map;
145}
146
147void Map::addEntity(EntityData& data)
148{
149 entities.push_back(data);
150}
151
diff --git a/src/map.h b/src/map.h deleted file mode 100644 index 22333aa..0000000 --- a/src/map.h +++ /dev/null
@@ -1,70 +0,0 @@
1#ifndef MAP_H
2#define MAP_H
3
4#include <string>
5#include <list>
6#include <map>
7
8class Entity;
9
10class Map {
11 public:
12 Map(int id);
13 Map() : Map(-1) {}
14 Map(const Map& map);
15 Map(Map&& map);
16 ~Map();
17 Map& operator= (Map other);
18 friend void swap(Map& first, Map& second);
19
20 enum class MoveType {
21 Wall,
22 Wrap,
23 Warp,
24 ReverseWarp
25 };
26
27 enum class MoveDir {
28 Left,
29 Right,
30 Up,
31 Down
32 };
33
34 struct EntityData {
35 std::string name;
36 std::pair<int, int> position;
37 std::map<std::string, int> items;
38 };
39
40 struct Adjacent {
41 MoveType type = MoveType::Wall;
42 int map = -1;
43 };
44
45 static MoveType moveTypeForShort(std::string str);
46 static MoveDir moveDirForShort(std::string str);
47 static bool moveTypeTakesMap(MoveType type);
48
49 int getID() const;
50 const int* getMapdata() const;
51 std::string getTitle() const;
52 const Adjacent& getAdjacent(MoveDir dir) const;
53
54 void createEntities(std::list<std::shared_ptr<Entity>>& entities) const;
55 bool operator==(const Map& other) const;
56 bool operator!=(const Map& other) const;
57
58 void setMapdata(int* mapdata);
59 void setTitle(std::string title);
60 void setAdjacent(MoveDir dir, MoveType type, int map);
61 void addEntity(EntityData& data);
62 private:
63 int* mapdata;
64 std::string title;
65 int id;
66 std::list<EntityData> entities;
67 std::map<MoveDir, Adjacent> adjacents;
68};
69
70#endif
diff --git a/src/muxer.cpp b/src/muxer.cpp index d831e6d..ecce82b 100644 --- a/src/muxer.cpp +++ b/src/muxer.cpp
@@ -4,6 +4,9 @@
4#include <portaudio.h> 4#include <portaudio.h>
5#include <list> 5#include <list>
6#include <cmath> 6#include <cmath>
7#include <vector>
8#include <stdexcept>
9#include <sstream>
7 10
8#define SAMPLE_RATE (44100) 11#define SAMPLE_RATE (44100)
9#define DELAY_IN_SECS (0.075) 12#define DELAY_IN_SECS (0.075)
@@ -17,11 +20,14 @@ const int delaySize = SAMPLE_RATE * DELAY_IN_SECS;
17class Sound { 20class Sound {
18 public: 21 public:
19 Sound(const char* filename, float vol); 22 Sound(const char* filename, float vol);
20 ~Sound(); 23
21 24 inline bool isDone() const
22 float* ptr; 25 {
26 return pos >= data.size();
27 }
28
29 std::vector<float> data;
23 unsigned long pos; 30 unsigned long pos;
24 unsigned long len;
25 float vol; 31 float vol;
26}; 32};
27 33
@@ -45,29 +51,29 @@ int paMuxerCallback(const void*, void* outputBuffer, unsigned long framesPerBuff
45{ 51{
46 Muxer* muxer = (Muxer*) userData; 52 Muxer* muxer = (Muxer*) userData;
47 float* out = (float*) outputBuffer; 53 float* out = (float*) outputBuffer;
48 54
49 for (unsigned long i = 0; i<framesPerBuffer; i++) 55 for (unsigned long i = 0; i<framesPerBuffer; i++)
50 { 56 {
51 float in = 0.0; 57 float in = 0.0;
52 58
53 for (auto& sound : muxer->playing) 59 for (auto& sound : muxer->playing)
54 { 60 {
55 if (sound.pos < sound.len) 61 if (sound.pos < sound.data.size())
56 { 62 {
57 in += sound.ptr[sound.pos++] * sound.vol; 63 in += sound.data[sound.pos++] * sound.vol;
58 } 64 }
59 } 65 }
60 66
61 if (in > 1) in = 1; 67 if (in > 1) in = 1;
62 if (in < -1) in = -1; 68 if (in < -1) in = -1;
63 69
64 float sample = muxer->delay[muxer->delayPos] * GAIN; 70 float sample = muxer->delay[muxer->delayPos] * GAIN;
65 muxer->delay[muxer->delayPos] = in + (muxer->delay[muxer->delayPos] * FEEDBACK); 71 muxer->delay[muxer->delayPos] = in + (muxer->delay[muxer->delayPos] * FEEDBACK);
66 muxer->delayPos++; 72 muxer->delayPos++;
67 if (muxer->delayPos > delaySize) muxer->delayPos = 0; 73 if (muxer->delayPos > delaySize) muxer->delayPos = 0;
68 *out++ = (in * DRY) + (sample * WET); 74 *out++ = (in * DRY) + (sample * WET);
69 } 75 }
70 76
71 return 0; 77 return 0;
72} 78}
73 79
@@ -76,11 +82,11 @@ static Muxer* muxer;
76void initMuxer() 82void initMuxer()
77{ 83{
78 muxer = new Muxer(); 84 muxer = new Muxer();
79 85
80 dealWithPaError(Pa_Initialize()); 86 dealWithPaError(Pa_Initialize());
81 dealWithPaError(Pa_OpenDefaultStream(&muxer->stream, 0, 1, paFloat32, SAMPLE_RATE, paFramesPerBufferUnspecified, paMuxerCallback, muxer)); 87 dealWithPaError(Pa_OpenDefaultStream(&muxer->stream, 0, 1, paFloat32, SAMPLE_RATE, paFramesPerBufferUnspecified, paMuxerCallback, muxer));
82 dealWithPaError(Pa_StartStream(muxer->stream)); 88 dealWithPaError(Pa_StartStream(muxer->stream));
83 89
84 muxer->delay = (float*) calloc(delaySize, sizeof(float)); 90 muxer->delay = (float*) calloc(delaySize, sizeof(float));
85} 91}
86 92
@@ -89,7 +95,7 @@ void destroyMuxer()
89 dealWithPaError(Pa_AbortStream(muxer->stream)); 95 dealWithPaError(Pa_AbortStream(muxer->stream));
90 dealWithPaError(Pa_CloseStream(muxer->stream)); 96 dealWithPaError(Pa_CloseStream(muxer->stream));
91 dealWithPaError(Pa_Terminate()); 97 dealWithPaError(Pa_Terminate());
92 98
93 free(muxer->delay); 99 free(muxer->delay);
94 delete muxer; 100 delete muxer;
95 muxer = 0; 101 muxer = 0;
@@ -98,8 +104,8 @@ void destroyMuxer()
98void playSound(const char* filename, float vol) 104void playSound(const char* filename, float vol)
99{ 105{
100 // First, clear out any sounds that have finished playing 106 // First, clear out any sounds that have finished playing
101 muxer->playing.remove_if([] (Sound& value) { return value.pos >= value.len; }); 107 muxer->playing.remove_if([] (Sound& value) { return value.isDone(); });
102 108
103 // Then, add the new sound 109 // Then, add the new sound
104 muxer->playing.emplace_back(filename, vol); 110 muxer->playing.emplace_back(filename, vol);
105} 111}
@@ -110,21 +116,18 @@ Sound::Sound(const char* filename, float vol)
110 SNDFILE* file = sf_open(filename, SFM_READ, &info); 116 SNDFILE* file = sf_open(filename, SFM_READ, &info);
111 if (file == nullptr) 117 if (file == nullptr)
112 { 118 {
113 printf("LibSndFile error: %s\n", sf_strerror(file)); 119 std::ostringstream errmsg;
114 exit(-1); 120 errmsg << "LibSndFile error: ";
121 errmsg << sf_strerror(file);
122
123 throw std::logic_error(errmsg.str());
115 } 124 }
116 125
117 ptr = (float*) malloc(info.frames * info.channels * sizeof(float)); 126 data.resize(info.frames * info.channels);
118 len = info.frames * info.channels;
119 pos = 0; 127 pos = 0;
120 this->vol = vol; 128 this->vol = vol;
121
122 sf_readf_float(file, ptr, info.frames);
123
124 sf_close(file);
125}
126 129
127Sound::~Sound() 130 sf_readf_float(file, data.data(), info.frames);
128{ 131
129 free(ptr); 132 sf_close(file);
130} 133}
diff --git a/src/renderer.cpp b/src/renderer.cpp deleted file mode 100644 index ffe9d47..0000000 --- a/src/renderer.cpp +++ /dev/null
@@ -1,861 +0,0 @@
1#include "renderer.h"
2#include <string>
3#include <fstream>
4#include <vector>
5#include <cstdio>
6#include <cstring>
7#include <cstdlib>
8#include <glm/glm.hpp>
9#include <glm/gtc/matrix_transform.hpp>
10#include "consts.h"
11
12// include stb_image
13#define STB_IMAGE_IMPLEMENTATION
14#define STBI_ONLY_PNG
15#define STBI_ONLY_BMP
16#include "stb_image.h"
17
18static bool rendererInitialized = false;
19
20static GLFWwindow* window;
21
22static GLuint generic_framebuffer; // The framebuffer
23static GLuint bloom_framebuffer;
24static GLuint bloom_depthbuffer;
25static int buffer_width = 1024;
26static int buffer_height = 768;
27
28static GLuint ntscShader; // The NTSC shader
29static GLuint finalShader; // The passthrough shader
30static GLuint blitShader; // The blitting shader
31static GLuint fillShader; // The fill shader
32static GLuint bloom1Shader;
33static GLuint bloom2Shader;
34
35// The buffers for the NTSC rendering process
36static GLuint renderedTex1;
37static GLuint renderedTex2;
38static GLuint renderedTexBufs[2];
39static int curBuf;
40static GLuint artifactsTex;
41static GLuint scanlinesTex;
42static GLuint preBloomTex;
43static GLuint bloomPassTex1;
44static GLuint bloomPassTex2;
45
46// The VAO
47static GLuint VertexArrayID;
48
49// A plane that fills the renderbuffer
50static GLuint quad_vertexbuffer;
51
52// Buffers for the mesh
53static GLuint mesh_vertexbuffer;
54static GLuint mesh_uvbuffer;
55static GLuint mesh_normalbuffer;
56static int mesh_numvertices;
57
58GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path)
59{
60 // Create the shaders
61 GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
62 GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
63
64 // Read the Vertex Shader code from the file
65 std::string VertexShaderCode;
66 std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
67 if(VertexShaderStream.is_open())
68 {
69 std::string Line = "";
70 while(getline(VertexShaderStream, Line))
71 VertexShaderCode += "\n" + Line;
72 VertexShaderStream.close();
73 }
74
75 // Read the Fragment Shader code from the file
76 std::string FragmentShaderCode;
77 std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
78 if(FragmentShaderStream.is_open()){
79 std::string Line = "";
80 while(getline(FragmentShaderStream, Line))
81 FragmentShaderCode += "\n" + Line;
82 FragmentShaderStream.close();
83 }
84
85 GLint Result = GL_FALSE;
86 int InfoLogLength;
87
88 // Compile Vertex Shader
89 printf("Compiling shader : %s\n", vertex_file_path);
90 char const * VertexSourcePointer = VertexShaderCode.c_str();
91 glShaderSource(VertexShaderID, 1, &VertexSourcePointer , nullptr);
92 glCompileShader(VertexShaderID);
93
94 // Check Vertex Shader
95 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
96 glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
97 std::vector<char> VertexShaderErrorMessage(InfoLogLength);
98 glGetShaderInfoLog(VertexShaderID, InfoLogLength, nullptr, &VertexShaderErrorMessage[0]);
99 fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
100
101 // Compile Fragment Shader
102 printf("Compiling shader : %s\n", fragment_file_path);
103 char const * FragmentSourcePointer = FragmentShaderCode.c_str();
104 glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , nullptr);
105 glCompileShader(FragmentShaderID);
106
107 // Check Fragment Shader
108 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
109 glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
110 std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
111 glGetShaderInfoLog(FragmentShaderID, InfoLogLength, nullptr, &FragmentShaderErrorMessage[0]);
112 fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
113
114 // Link the program
115 fprintf(stdout, "Linking program\n");
116 GLuint ProgramID = glCreateProgram();
117 glAttachShader(ProgramID, VertexShaderID);
118 glAttachShader(ProgramID, FragmentShaderID);
119 glLinkProgram(ProgramID);
120
121 // Check the program
122 glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
123 glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
124 std::vector<char> ProgramErrorMessage( glm::max(InfoLogLength, int(1)) );
125 glGetProgramInfoLog(ProgramID, InfoLogLength, nullptr, &ProgramErrorMessage[0]);
126 fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
127
128 glDeleteShader(VertexShaderID);
129 glDeleteShader(FragmentShaderID);
130
131 return ProgramID;
132}
133
134void flipImageData(unsigned char* data, int width, int height, int comps)
135{
136 unsigned char* data_copy = (unsigned char*) malloc(width*height*comps*sizeof(unsigned char));
137 memcpy(data_copy, data, width*height*comps);
138
139 int row_size = width * comps;
140
141 for (int i=0;i<height;i++)
142 {
143 memcpy(data + (row_size*i), data_copy + (row_size*(height-i-1)), row_size);
144 }
145
146 free(data_copy);
147}
148
149void loadMesh(const char* filename, std::vector<glm::vec3>& out_vertices, std::vector<glm::vec2>& out_uvs, std::vector<glm::vec3>& out_normals)
150{
151 FILE* file = fopen(filename, "r");
152 if (file == nullptr)
153 {
154 fprintf(stderr, "Could not open mesh file %s\n", filename);
155 exit(1);
156 }
157
158 std::vector<glm::vec3> temp_vertices;
159 std::vector<glm::vec2> temp_uvs;
160 std::vector<glm::vec3> temp_normals;
161
162 for (;;)
163 {
164 char lineHeader[256];
165 int res = fscanf(file, "%s", lineHeader);
166 if (res == EOF)
167 {
168 break;
169 }
170
171 if (!strncmp(lineHeader, "v", 2))
172 {
173 glm::vec3 vertex;
174 fscanf(file, "%f %f %f\n", &vertex.x,&vertex.y,&vertex.z);
175 temp_vertices.push_back(vertex);
176 } else if (!strncmp(lineHeader, "vt", 3))
177 {
178 glm::vec2 uv;
179 fscanf(file, "%f %f\n", &uv.x, &uv.y);
180 temp_uvs.push_back(uv);
181 } else if (!strncmp(lineHeader, "vn", 3))
182 {
183 glm::vec3 normal;
184 fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
185 temp_normals.push_back(normal);
186 } else if (!strncmp(lineHeader, "f", 2))
187 {
188 int vertexIDs[3], uvIDs[3], normalIDs[3];
189 fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIDs[0], &uvIDs[0], &normalIDs[0], &vertexIDs[1], &uvIDs[1], &normalIDs[1], &vertexIDs[2], &uvIDs[2], &normalIDs[2]);
190
191 for (int i=0; i<3; i++)
192 {
193 out_vertices.push_back(temp_vertices[vertexIDs[i] - 1]);
194 out_uvs.push_back(temp_uvs[uvIDs[i] - 1]);
195 out_normals.push_back(temp_normals[normalIDs[i] - 1]);
196 }
197 }
198 }
199}
200
201void setFramebufferSize(GLFWwindow* w, int width, int height)
202{
203 buffer_width = width;
204 buffer_height = height;
205
206 glDeleteFramebuffers(1, &bloom_framebuffer);
207 glDeleteRenderbuffers(1, &bloom_depthbuffer);
208
209 glGenFramebuffers(1, &bloom_framebuffer);
210 glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer);
211 GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT1};
212 glDrawBuffers(1, DrawBuffers);
213
214 glGenRenderbuffers(1, &bloom_depthbuffer);
215 glBindRenderbuffer(GL_RENDERBUFFER, bloom_depthbuffer);
216 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
217 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bloom_depthbuffer);
218
219 glDeleteTextures(1, &preBloomTex);
220 glDeleteTextures(1, &bloomPassTex1);
221 glDeleteTextures(1, &bloomPassTex2);
222
223 glGenTextures(1, &preBloomTex);
224 glBindTexture(GL_TEXTURE_2D, preBloomTex);
225 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
227 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
228 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
229 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
230
231 glGenTextures(1, &bloomPassTex1);
232 glBindTexture(GL_TEXTURE_2D, bloomPassTex1);
233 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width/4, height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
234 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
235 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
236 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
238
239 glGenTextures(1, &bloomPassTex2);
240 glBindTexture(GL_TEXTURE_2D, bloomPassTex2);
241 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width/4, height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
242 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
243 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
244 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
246}
247
248GLFWwindow* initRenderer()
249{
250 if (rendererInitialized)
251 {
252 fprintf(stderr, "Renderer already initialized\n");
253 exit(-1);
254 }
255
256 // Initialize GLFW
257 if (!glfwInit())
258 {
259 fprintf(stderr, "Failed to initialize GLFW\n");
260 exit(-1);
261 }
262
263 glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing
264 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want version 3.3
265 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
266 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac requires this
267 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
268
269 // Create a window
270 window = glfwCreateWindow(1024, 768, "Aromatherapy", nullptr, nullptr);
271 if (window == nullptr)
272 {
273 fprintf(stderr, "Failed to open GLFW window\n");
274 glfwTerminate();
275 exit(-1);
276 }
277
278 glfwMakeContextCurrent(window);
279 glewExperimental = true; // Needed in core profile
280 if (glewInit() != GLEW_OK)
281 {
282 fprintf(stderr, "Failed to initialize GLEW\n");
283 exit(-1);
284 }
285
286 glfwSetFramebufferSizeCallback(window, &setFramebufferSize);
287
288 // Set up vertex array object
289 glGenVertexArrays(1, &VertexArrayID);
290 glBindVertexArray(VertexArrayID);
291
292 // Enable depth testing
293 glEnable(GL_DEPTH_TEST);
294 glDepthFunc(GL_LESS);
295
296 // Enable blending
297 glEnable(GL_BLEND);
298 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
299
300 // Set up the framebuffer
301 glGenFramebuffers(1, &generic_framebuffer);
302 glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer);
303 GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
304 glDrawBuffers(1, DrawBuffers);
305
306 glGenFramebuffers(1, &bloom_framebuffer);
307 glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer);
308 GLenum DrawBuffers2[1] = {GL_COLOR_ATTACHMENT1};
309 glDrawBuffers(1, DrawBuffers2);
310
311 glGenRenderbuffers(1, &bloom_depthbuffer);
312 glBindRenderbuffer(GL_RENDERBUFFER, bloom_depthbuffer);
313 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 1024, 768);
314 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bloom_depthbuffer);
315
316 // Set up the NTSC rendering buffers
317 glGenTextures(1, &renderedTex1);
318 glBindTexture(GL_TEXTURE_2D, renderedTex1);
319 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GAME_WIDTH, GAME_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
320 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
321 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
322 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
324 renderedTexBufs[0] = renderedTex1;
325
326 glGenTextures(1, &renderedTex2);
327 glBindTexture(GL_TEXTURE_2D, renderedTex2);
328 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GAME_WIDTH, GAME_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
330 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
331 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
332 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
333 renderedTexBufs[1] = renderedTex2;
334
335 // Set up bloom rendering buffers
336 glGenTextures(1, &preBloomTex);
337 glBindTexture(GL_TEXTURE_2D, preBloomTex);
338 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 768, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
339 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
340 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
343
344 glGenTextures(1, &bloomPassTex1);
345 glBindTexture(GL_TEXTURE_2D, bloomPassTex1);
346 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024/4, 768/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
347 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
348 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
349 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
350 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
351
352 glGenTextures(1, &bloomPassTex2);
353 glBindTexture(GL_TEXTURE_2D, bloomPassTex2);
354 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024/4, 768/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
355 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
356 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
357 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
359
360 curBuf = 0;
361
362 // Load the mesh!
363 std::vector<glm::vec3> mesh_vertices;
364 std::vector<glm::vec2> mesh_uvs;
365 std::vector<glm::vec3> mesh_normals;
366 loadMesh("res/monitor-old.obj", mesh_vertices, mesh_uvs, mesh_normals);
367
368 mesh_numvertices = mesh_vertices.size();
369
370 glGenBuffers(1, &mesh_vertexbuffer);
371 glBindBuffer(GL_ARRAY_BUFFER, mesh_vertexbuffer);
372 glBufferData(GL_ARRAY_BUFFER, mesh_vertices.size() * sizeof(glm::vec3), &mesh_vertices[0], GL_STATIC_DRAW);
373
374 glGenBuffers(1, &mesh_uvbuffer);
375 glBindBuffer(GL_ARRAY_BUFFER, mesh_uvbuffer);
376 glBufferData(GL_ARRAY_BUFFER, mesh_uvs.size() * sizeof(glm::vec3), &mesh_uvs[0], GL_STATIC_DRAW);
377
378 glGenBuffers(1, &mesh_normalbuffer);
379 glBindBuffer(GL_ARRAY_BUFFER, mesh_normalbuffer);
380 glBufferData(GL_ARRAY_BUFFER, mesh_normals.size() * sizeof(glm::vec3), &mesh_normals[0], GL_STATIC_DRAW);
381
382 // Load the vertices of a flat surface
383 GLfloat g_quad_vertex_buffer_data[] = {
384 -1.0f, -1.0f, 0.0f,
385 1.0f, -1.0f, 0.0f,
386 -1.0f, 1.0f, 0.0f,
387 -1.0f, 1.0f, 0.0f,
388 1.0f, -1.0f, 0.0f,
389 1.0f, 1.0f, 0.0f,
390 };
391
392 glGenBuffers(1, &quad_vertexbuffer);
393 glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
394 glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW);
395
396 glGenTextures(1, &artifactsTex);
397 glBindTexture(GL_TEXTURE_2D, artifactsTex);
398 int atdw, atdh;
399 unsigned char* artifactsTex_data = stbi_load("res/artifacts.bmp", &atdw, &atdh, 0, 3);
400 flipImageData(artifactsTex_data, atdw, atdh, 3);
401 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, atdw, atdh, 0, GL_RGB, GL_UNSIGNED_BYTE, artifactsTex_data);
402 stbi_image_free(artifactsTex_data);
403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
404 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
405 glGenerateMipmap(GL_TEXTURE_2D);
406
407 glGenTextures(1, &scanlinesTex);
408 glBindTexture(GL_TEXTURE_2D, scanlinesTex);
409 int stdw, stdh;
410 unsigned char* scanlinesTex_data = stbi_load("res/scanlines_333.bmp", &stdw, &stdh, 0, 3);
411 flipImageData(scanlinesTex_data, stdw, stdh, 3);
412 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, atdw, atdh, 0, GL_RGB, GL_UNSIGNED_BYTE, scanlinesTex_data);
413 stbi_image_free(scanlinesTex_data);
414 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
415 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
416 glGenerateMipmap(GL_TEXTURE_2D);
417
418 // Load the shaders
419 ntscShader = LoadShaders("shaders/ntsc.vertex", "shaders/ntsc.fragment");
420 finalShader = LoadShaders("shaders/final.vertex", "shaders/final.fragment");
421 blitShader = LoadShaders("shaders/blit.vertex", "shaders/blit.fragment");
422 fillShader = LoadShaders("shaders/fill.vertex", "shaders/fill.fragment");
423 bloom1Shader = LoadShaders("shaders/bloom1.vertex", "shaders/bloom1.fragment");
424 bloom2Shader = LoadShaders("shaders/bloom2.vertex", "shaders/bloom2.fragment");
425
426 rendererInitialized = true;
427
428 return window;
429}
430
431void destroyRenderer()
432{
433 if (!rendererInitialized)
434 {
435 fprintf(stderr, "Renderer not initialized\n");
436 exit(-1);
437 }
438
439 // Delete the plane buffer
440 glDeleteBuffers(1, &quad_vertexbuffer);
441 glDeleteBuffers(1, &mesh_vertexbuffer);
442 glDeleteBuffers(1, &mesh_uvbuffer);
443 glDeleteBuffers(1, &mesh_normalbuffer);
444
445 // Delete the shaders
446 glDeleteProgram(ntscShader);
447 glDeleteProgram(finalShader);
448 glDeleteProgram(blitShader);
449 glDeleteProgram(fillShader);
450 glDeleteProgram(bloom1Shader);
451 glDeleteProgram(bloom2Shader);
452
453 // Delete the NTSC rendering buffers
454 glDeleteTextures(1, &renderedTex1);
455 glDeleteTextures(1, &renderedTex2);
456 glDeleteTextures(1, &artifactsTex);
457 glDeleteTextures(1, &scanlinesTex);
458 glDeleteTextures(1, &preBloomTex);
459 glDeleteTextures(1, &bloomPassTex1);
460 glDeleteTextures(1, &bloomPassTex2);
461
462 // Delete the framebuffer
463 glDeleteRenderbuffers(1, &bloom_depthbuffer);
464 glDeleteFramebuffers(1, &bloom_framebuffer);
465 glDeleteFramebuffers(1, &generic_framebuffer);
466
467 // Delete the VAO
468 glDeleteVertexArrays(1, &VertexArrayID);
469
470 // Kill the window
471 glfwTerminate();
472
473 rendererInitialized = false;
474}
475
476Texture::Texture(int width, int height)
477{
478 if (!rendererInitialized)
479 {
480 fprintf(stderr, "Renderer not initialized\n");
481 exit(-1);
482 }
483
484 this->width = width;
485 this->height = height;
486
487 glGenTextures(1, &texID);
488 glBindTexture(GL_TEXTURE_2D, texID);
489 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
490 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
491 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
492 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
493 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
494}
495
496Texture::Texture(const char* filename)
497{
498 if (!rendererInitialized)
499 {
500 fprintf(stderr, "Renderer not initialized\n");
501 exit(-1);
502 }
503
504 glGenTextures(1, &texID);
505 glBindTexture(GL_TEXTURE_2D, texID);
506 unsigned char* data = stbi_load(filename, &width, &height, 0, 4);
507 flipImageData(data, width, height, 4);
508 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
509 stbi_image_free(data);
510 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
511 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
512 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
514}
515
516Texture::Texture(const Texture& tex)
517{
518 if (!rendererInitialized)
519 {
520 fprintf(stderr, "Renderer not initialized\n");
521 exit(-1);
522 }
523
524 width = tex.width;
525 height = tex.height;
526
527 unsigned char* data = (unsigned char*) malloc(4 * width * height);
528 glBindTexture(GL_TEXTURE_2D, tex.texID);
529 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
530
531 glGenTextures(1, &texID);
532 glBindTexture(GL_TEXTURE_2D, texID);
533 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
534 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
535 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
537 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
538
539 free(data);
540}
541
542Texture::Texture(Texture&& tex) : Texture(0, 0)
543{
544 swap(*this, tex);
545}
546
547Texture::~Texture()
548{
549 if (!rendererInitialized)
550 {
551 fprintf(stderr, "Renderer not initialized\n");
552 exit(-1);
553 }
554
555 glDeleteTextures(1, &texID);
556}
557
558Texture& Texture::operator= (Texture tex)
559{
560 swap(*this, tex);
561
562 return *this;
563}
564
565void swap(Texture& tex1, Texture& tex2)
566{
567 std::swap(tex1.width, tex2.width);
568 std::swap(tex1.height, tex2.height);
569 std::swap(tex1.texID, tex2.texID);
570}
571
572void Texture::fill(Rectangle dstrect, int r, int g, int b)
573{
574 if (!rendererInitialized)
575 {
576 fprintf(stderr, "Renderer not initialized\n");
577 exit(-1);
578 }
579
580 // Target the framebuffer
581 glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer);
582 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texID, 0);
583
584 // Set up the vertex attributes
585 GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0;
586 GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0);
587 GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0;
588 GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0);
589
590 GLfloat vertexbuffer_data[] = {
591 minx, miny,
592 maxx, miny,
593 maxx, maxy,
594 minx, miny,
595 minx, maxy,
596 maxx, maxy
597 };
598 GLuint vertexbuffer;
599 glGenBuffers(1, &vertexbuffer);
600 glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
601 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexbuffer_data), vertexbuffer_data, GL_STATIC_DRAW);
602 glEnableVertexAttribArray(0);
603 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
604
605 glViewport(0, 0, width, height);
606 glClear(GL_DEPTH_BUFFER_BIT);
607 glUseProgram(fillShader);
608 glUniform3f(glGetUniformLocation(fillShader, "vecColor"), r / 255.0, g / 255.0, b / 255.0);
609
610 glDrawArrays(GL_TRIANGLES, 0, 6);
611
612 glDisableVertexAttribArray(0);
613 glDeleteBuffers(1, &vertexbuffer);
614}
615
616void Texture::blit(const Texture& srctex, Rectangle srcrect, Rectangle dstrect, double alpha)
617{
618 if (!rendererInitialized)
619 {
620 fprintf(stderr, "Renderer not initialized\n");
621 exit(-1);
622 }
623
624 alpha = glm::clamp(alpha, 0.0, 1.0);
625
626 // Target the framebuffer
627 glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer);
628 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texID, 0);
629
630 // Set up the vertex attributes
631 GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0;
632 GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0);
633 GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0;
634 GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0);
635
636 GLfloat vertexbuffer_data[] = {
637 minx, miny,
638 maxx, miny,
639 minx, maxy,
640 maxx, maxy
641 };
642 GLuint vertexbuffer;
643 glGenBuffers(1, &vertexbuffer);
644 glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
645 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexbuffer_data), vertexbuffer_data, GL_STATIC_DRAW);
646 glEnableVertexAttribArray(0);
647 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
648
649 GLfloat minu = (GLfloat) srcrect.x / srctex.width;
650 GLfloat minv = 1 - ((GLfloat) srcrect.y / srctex.height);
651 GLfloat maxu = (GLfloat) (srcrect.x + srcrect.w) / srctex.width;
652 GLfloat maxv = 1 - ((GLfloat) (srcrect.y + srcrect.h) / srctex.height);
653
654 GLfloat texcoordbuffer_data[] = {
655 minu, minv,
656 maxu, minv,
657 minu, maxv,
658 maxu, maxv
659 };
660 GLuint texcoordbuffer;
661 glGenBuffers(1, &texcoordbuffer);
662 glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer);
663 glBufferData(GL_ARRAY_BUFFER, sizeof(texcoordbuffer_data), texcoordbuffer_data, GL_STATIC_DRAW);
664 glEnableVertexAttribArray(1);
665 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
666
667 // Set up the shader
668 glUseProgram(blitShader);
669 glClear(GL_DEPTH_BUFFER_BIT);
670 glViewport(0, 0, width, height);
671
672 glActiveTexture(GL_TEXTURE0);
673 glBindTexture(GL_TEXTURE_2D, srctex.texID);
674 glUniform1i(glGetUniformLocation(blitShader, "srctex"), 0);
675 glUniform1f(glGetUniformLocation(blitShader, "alpha"), alpha);
676
677 // Blit!
678 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
679
680 // Unload everything
681 glDisableVertexAttribArray(1);
682 glDisableVertexAttribArray(0);
683 glDeleteBuffers(1, &texcoordbuffer);
684 glDeleteBuffers(1, &vertexbuffer);
685}
686
687void bloomPass1(GLuint srcTex, GLuint dstTex, bool horizontal, glm::vec2 srcRes, glm::vec2 dstRes)
688{
689 glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer);
690 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dstTex, 0);
691 glViewport(0,0,dstRes.x,dstRes.y);
692 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
693 glUseProgram(bloom1Shader);
694
695 glActiveTexture(GL_TEXTURE0);
696 glBindTexture(GL_TEXTURE_2D, srcTex);
697 glUniform1i(glGetUniformLocation(bloom1Shader, "inTex"), 0);
698
699 glm::vec2 offset = glm::vec2(0.0);
700 if (horizontal)
701 {
702 offset.x = 1.2/srcRes.x;
703 } else {
704 offset.y = 1.2/srcRes.y;
705 }
706
707 glUniform2f(glGetUniformLocation(bloom1Shader, "offset"), offset.x, offset.y);
708
709 glEnableVertexAttribArray(0);
710 glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
711 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
712 glDrawArrays(GL_TRIANGLES, 0, 6);
713 glDisableVertexAttribArray(0);
714}
715
716void Texture::renderScreen() const
717{
718 if (!rendererInitialized)
719 {
720 fprintf(stderr, "Renderer not initialized\n");
721 exit(-1);
722 }
723
724 // First we're going to composite our frame with the previous frame
725 // We start by setting up the framebuffer
726 glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer);
727 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexBufs[curBuf], 0);
728
729 // Set up the shader
730 glViewport(0,0,GAME_WIDTH,GAME_HEIGHT);
731 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
732 glUseProgram(ntscShader);
733
734 // Use the current frame texture, nearest neighbor and clamped to edge
735 glActiveTexture(GL_TEXTURE0);
736 glBindTexture(GL_TEXTURE_2D, texID);
737 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
738 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
739 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
740 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
741 glUniform1i(glGetUniformLocation(ntscShader, "curFrameSampler"), 0);
742
743 // Use the previous frame composite texture, nearest neighbor and clamped to edge
744 glActiveTexture(GL_TEXTURE1);
745 glBindTexture(GL_TEXTURE_2D, renderedTexBufs[(curBuf + 1) % 2]);
746 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
747 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
748 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
749 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
750 glUniform1i(glGetUniformLocation(ntscShader, "prevFrameSampler"), 1);
751
752 // Load the NTSC artifact texture
753 glActiveTexture(GL_TEXTURE2);
754 glBindTexture(GL_TEXTURE_2D, artifactsTex);
755 glUniform1i(glGetUniformLocation(ntscShader, "NTSCArtifactSampler"), 2);
756 glUniform1f(glGetUniformLocation(ntscShader, "NTSCLerp"), curBuf * 1.0);
757
758 if ((rand() % 60) == 0)
759 {
760 // Change the 0.0 to a 1.0 or a 10.0 for a glitchy effect!
761 glUniform1f(glGetUniformLocation(ntscShader, "Tuning_NTSC"), 0.0);
762 } else {
763 glUniform1f(glGetUniformLocation(ntscShader, "Tuning_NTSC"), 0.0);
764 }
765
766 // Render our composition
767 glEnableVertexAttribArray(0);
768 glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
769 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
770 glDrawArrays(GL_TRIANGLES, 0, 6);
771 glDisableVertexAttribArray(0);
772
773 // We're going to render the screen now
774 glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer);
775 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, preBloomTex, 0);
776 glViewport(0,0,buffer_width,buffer_height);
777 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
778 glUseProgram(finalShader);
779
780 // Use the composited frame texture, linearly filtered and filling in black for the border
781 glActiveTexture(GL_TEXTURE0);
782 glBindTexture(GL_TEXTURE_2D, renderedTexBufs[curBuf]);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
784 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
787 float border_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
788 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
789 glGenerateMipmap(GL_TEXTURE_2D);
790 glUniform1i(glGetUniformLocation(finalShader, "rendertex"), 0);
791
792 // Use the scanlines texture
793 glActiveTexture(GL_TEXTURE1);
794 glBindTexture(GL_TEXTURE_2D, scanlinesTex);
795 glUniform1i(glGetUniformLocation(finalShader, "scanlinestex"), 1);
796
797 // Initialize the MVP matrices
798 glm::mat4 p_matrix = glm::perspective(glm::radians(25.0f), (float) buffer_width / (float) buffer_height, 0.1f, 100.0f);
799 glm::mat4 v_matrix = glm::lookAt(glm::vec3(3.75,0,0), glm::vec3(0,0,0), glm::vec3(0,1,0));
800 glm::mat4 m_matrix = glm::mat4(1.0);
801 glm::mat4 mvp_matrix = p_matrix * v_matrix * m_matrix;
802
803 glUniformMatrix4fv(glGetUniformLocation(finalShader, "MVP"), 1, GL_FALSE, &mvp_matrix[0][0]);
804 glUniformMatrix4fv(glGetUniformLocation(finalShader, "worldMat"), 1, GL_FALSE, &m_matrix[0][0]);
805 glUniform2f(glGetUniformLocation(finalShader, "resolution"), buffer_width, buffer_height);
806 glUniform1f(glGetUniformLocation(finalShader, "iGlobalTime"), glfwGetTime());
807 glUniform3f(glGetUniformLocation(finalShader, "frameColor"), 0.76f, 0.78f, 0.81f);
808
809 glEnableVertexAttribArray(0);
810 glBindBuffer(GL_ARRAY_BUFFER, mesh_vertexbuffer);
811 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
812
813 glEnableVertexAttribArray(1);
814 glBindBuffer(GL_ARRAY_BUFFER, mesh_normalbuffer);
815 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
816
817 glEnableVertexAttribArray(2);
818 glBindBuffer(GL_ARRAY_BUFFER, mesh_uvbuffer);
819 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
820
821 glDrawArrays(GL_TRIANGLES, 0, mesh_numvertices);
822 glDisableVertexAttribArray(2);
823 glDisableVertexAttribArray(1);
824 glDisableVertexAttribArray(0);
825
826 // First pass of bloom!
827 glm::vec2 buffer_size = glm::vec2(buffer_width, buffer_height);
828 bloomPass1(preBloomTex, bloomPassTex1, true, buffer_size, buffer_size / 4.0f);
829 bloomPass1(bloomPassTex1, bloomPassTex2, false, buffer_size / 4.0f, buffer_size / 4.0f);
830
831 // Do the second pass of bloom and render to screen
832 glBindFramebuffer(GL_FRAMEBUFFER, 0);
833 glViewport(0, 0, buffer_width, buffer_height);
834 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
835 glUseProgram(bloom2Shader);
836
837 glActiveTexture(GL_TEXTURE0);
838 glBindTexture(GL_TEXTURE_2D, preBloomTex);
839 glUniform1i(glGetUniformLocation(bloom2Shader, "clearTex"), 0);
840
841 glActiveTexture(GL_TEXTURE1);
842 glBindTexture(GL_TEXTURE_2D, bloomPassTex2);
843 glUniform1i(glGetUniformLocation(bloom2Shader, "blurTex"), 1);
844
845 glUniform1f(glGetUniformLocation(bloom2Shader, "iGlobalTime"), glfwGetTime());
846
847 glEnableVertexAttribArray(0);
848 glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
849 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
850 glDrawArrays(GL_TRIANGLES, 0, 6);
851 glDisableVertexAttribArray(0);
852
853 glfwSwapBuffers(window);
854
855 curBuf = (curBuf + 1) % 2;
856}
857
858Rectangle Texture::entirety() const
859{
860 return {0, 0, width, height};
861}
diff --git a/src/renderer.h b/src/renderer.h deleted file mode 100644 index 84ad688..0000000 --- a/src/renderer.h +++ /dev/null
@@ -1,37 +0,0 @@
1#include <GL/glew.h>
2#include <GLFW/glfw3.h>
3
4#ifndef RENDERER_H
5#define RENDERER_H
6
7struct Rectangle {
8 int x;
9 int y;
10 int w;
11 int h;
12};
13
14class Texture {
15 public:
16 Texture(int width, int height);
17 Texture(const char* file);
18 Texture(const Texture& tex);
19 Texture(Texture&& tex);
20 ~Texture();
21 Texture& operator= (Texture tex);
22 friend void swap(Texture& tex1, Texture& tex2);
23 void fill(Rectangle loc, int r, int g, int b);
24 void blit(const Texture& src, Rectangle srcrect, Rectangle dstrect, double alpha = 1.0);
25 void renderScreen() const;
26 Rectangle entirety() const;
27
28 private:
29 GLuint texID;
30 int width;
31 int height;
32};
33
34GLFWwindow* initRenderer();
35void destroyRenderer();
36
37#endif
diff --git a/src/renderer/gl.h b/src/renderer/gl.h new file mode 100644 index 0000000..4e98c42 --- /dev/null +++ b/src/renderer/gl.h
@@ -0,0 +1,7 @@
1#ifndef GL_H_3EE4A268
2#define GL_H_3EE4A268
3
4#include <GL/glew.h>
5#include <GLFW/glfw3.h>
6
7#endif /* end of include guard: GL_H_3EE4A268 */
diff --git a/src/renderer/mesh.cpp b/src/renderer/mesh.cpp new file mode 100644 index 0000000..b06b723 --- /dev/null +++ b/src/renderer/mesh.cpp
@@ -0,0 +1,109 @@
1#include "mesh.h"
2#include <fstream>
3#include <vector>
4#include <map>
5#include <glm/glm.hpp>
6#include "util.h"
7
8Mesh::Mesh(std::string filename)
9{
10 std::ifstream meshfile(filename);
11 if (!meshfile.is_open())
12 {
13 throw std::invalid_argument("Could not open mesh file");
14 }
15
16 std::vector<glm::vec3> tempVertices;
17 std::vector<glm::vec2> tempUvs;
18 std::vector<glm::vec3> tempNormals;
19
20 std::vector<glm::vec3> outVertices;
21 std::vector<glm::vec2> outUvs;
22 std::vector<glm::vec3> outNormals;
23 std::map<element, unsigned short> elementIds;
24 std::vector<unsigned short> indices;
25
26 while (meshfile)
27 {
28 std::string linetype;
29 meshfile >> linetype;
30
31 if (linetype == "v")
32 {
33 glm::vec3 vertex;
34 meshfile >> vertex.x >> vertex.y >> vertex.z;
35
36 tempVertices.push_back(std::move(vertex));
37 } else if (linetype == "vt")
38 {
39 glm::vec2 uv;
40 meshfile >> uv.x >> uv.y;
41
42 tempUvs.push_back(std::move(uv));
43 } else if (linetype == "vn")
44 {
45 glm::vec3 normal;
46 meshfile >> normal.x >> normal.y >> normal.z;
47
48 tempNormals.push_back(std::move(normal));
49 } else if (linetype == "f")
50 {
51 element elements[3];
52
53 meshfile
54 >> elements[0].vertexId >> chlit('/')
55 >> elements[0].uvId >> chlit('/')
56 >> elements[0].normalId
57 >> elements[1].vertexId >> chlit('/')
58 >> elements[1].uvId >> chlit('/')
59 >> elements[1].normalId
60 >> elements[2].vertexId >> chlit('/')
61 >> elements[2].uvId >> chlit('/')
62 >> elements[2].normalId;
63
64 for (size_t i = 0; i < 3; i++)
65 {
66 if (!elementIds.count(elements[i]))
67 {
68 elementIds[elements[i]] = outVertices.size();
69
70 outVertices.push_back(tempVertices[elements[i].vertexId - 1]);
71 outUvs.push_back(tempUvs[elements[i].uvId - 1]);
72 outNormals.push_back(tempNormals[elements[i].normalId - 1]);
73 }
74
75 indices.push_back(elementIds[elements[i]]);
76 }
77 }
78 }
79
80 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer_.getId());
81 glBufferData(
82 GL_ARRAY_BUFFER,
83 outVertices.size() * sizeof(glm::vec3),
84 outVertices.data(),
85 GL_STATIC_DRAW);
86
87 glBindBuffer(GL_ARRAY_BUFFER, uvBuffer_.getId());
88 glBufferData(
89 GL_ARRAY_BUFFER,
90 outUvs.size() * sizeof(glm::vec2),
91 outUvs.data(),
92 GL_STATIC_DRAW);
93
94 glBindBuffer(GL_ARRAY_BUFFER, normalBuffer_.getId());
95 glBufferData(
96 GL_ARRAY_BUFFER,
97 outNormals.size() * sizeof(glm::vec3),
98 outNormals.data(),
99 GL_STATIC_DRAW);
100
101 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer_.getId());
102 glBufferData(
103 GL_ELEMENT_ARRAY_BUFFER,
104 indices.size() * sizeof(unsigned short),
105 indices.data(),
106 GL_STATIC_DRAW);
107
108 indexCount_ = indices.size();
109}
diff --git a/src/renderer/mesh.h b/src/renderer/mesh.h new file mode 100644 index 0000000..06bf65d --- /dev/null +++ b/src/renderer/mesh.h
@@ -0,0 +1,62 @@
1#ifndef MESH_H_76B72E12
2#define MESH_H_76B72E12
3
4#include <string>
5#include "gl.h"
6#include "wrappers.h"
7
8class Mesh {
9public:
10
11 Mesh(std::string filename);
12
13 Mesh(const Mesh& other) = delete;
14 Mesh& operator=(const Mesh& other) = delete;
15
16 inline GLuint getVertexBufferId() const
17 {
18 return vertexBuffer_.getId();
19 }
20
21 inline GLuint getUvBufferId() const
22 {
23 return uvBuffer_.getId();
24 }
25
26 inline GLuint getNormalBufferId() const
27 {
28 return normalBuffer_.getId();
29 }
30
31 inline GLuint getIndexBufferId() const
32 {
33 return indexBuffer_.getId();
34 }
35
36 inline size_t getIndexCount() const
37 {
38 return indexCount_;
39 }
40
41private:
42
43 struct element {
44 size_t vertexId;
45 size_t uvId;
46 size_t normalId;
47
48 bool operator<(const element& other) const
49 {
50 return std::tie(vertexId, uvId, normalId) <
51 std::tie(other.vertexId, other.uvId, other.normalId);
52 }
53 };
54
55 GLBuffer vertexBuffer_;
56 GLBuffer uvBuffer_;
57 GLBuffer normalBuffer_;
58 GLBuffer indexBuffer_;
59 size_t indexCount_;
60};
61
62#endif /* end of include guard: MESH_H_76B72E12 */
diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp new file mode 100644 index 0000000..30ef787 --- /dev/null +++ b/src/renderer/renderer.cpp
@@ -0,0 +1,630 @@
1#include "renderer.h"
2#include <glm/gtc/matrix_transform.hpp>
3#include <stb_image.h>
4#include "consts.h"
5#include "game.h"
6#include "texture.h"
7
8void setFramebufferSize(GLFWwindow* w, int width, int height)
9{
10 Game& game = *static_cast<Game*>(glfwGetWindowUserPointer(w));
11 Renderer& renderer = game.getRenderer();
12
13 renderer.width_ = width;
14 renderer.height_ = height;
15
16 renderer.bloomFb_ = {};
17 renderer.bloomDepth_ = {};
18 renderer.preBloomTex_ = {};
19 renderer.bloomPassTex1_ = {};
20 renderer.bloomPassTex2_ = {};
21
22 renderer.initializeFramebuffers();
23}
24
25bool Renderer::singletonInitialized_ = false;
26
27Renderer::Window::Window()
28{
29 // Initialize GLFW
30 if (!glfwInit())
31 {
32 throw std::runtime_error("Failed to initialize GLFW");
33 }
34
35 glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing
36 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want version 3.3
37 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
38 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac requires this
39 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
40
41 // Create a window
42 window_ = glfwCreateWindow(1024, 768, "Aromatherapy", nullptr, nullptr);
43 if (window_ == nullptr)
44 {
45 throw std::runtime_error("Failed to open GLFW window");
46 }
47
48 glfwMakeContextCurrent(window_);
49
50 glewExperimental = true; // Needed in core profile
51 if (glewInit() != GLEW_OK)
52 {
53 throw std::runtime_error("Failed to initialize GLEW");
54 }
55
56 glfwSetFramebufferSizeCallback(window_, &setFramebufferSize);
57}
58
59Renderer::Window::~Window()
60{
61 glfwTerminate();
62}
63
64Renderer::Renderer() :
65 monitor_("res/monitor-old.obj"),
66 ntscShader_("ntsc"),
67 finalShader_("final"),
68 blitShader_("blit"),
69 fillShader_("fill"),
70 bloom1Shader_("bloom1"),
71 bloom2Shader_("bloom2")
72{
73 if (singletonInitialized_)
74 {
75 throw std::logic_error("Singleton renderer already initialized");
76 }
77
78 singletonInitialized_ = true;
79
80 // Set up vertex array object
81 glBindVertexArray(vao_.getId());
82
83 // Enable depth testing
84 glEnable(GL_DEPTH_TEST);
85 glDepthFunc(GL_LESS);
86
87 // Enable blending
88 glEnable(GL_BLEND);
89 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
90
91 // Set up the rendering buffers and textures
92 glfwGetFramebufferSize(window_.getHandle(), &width_, &height_);
93
94 initializeFramebuffers();
95
96 // Load the vertices of a flat surface
97 GLfloat g_quad_vertex_buffer_data[] = {
98 -1.0f, -1.0f, 0.0f,
99 1.0f, -1.0f, 0.0f,
100 -1.0f, 1.0f, 0.0f,
101 -1.0f, 1.0f, 0.0f,
102 1.0f, -1.0f, 0.0f,
103 1.0f, 1.0f, 0.0f,
104 };
105
106 glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId());
107 glBufferData(
108 GL_ARRAY_BUFFER,
109 sizeof(GLfloat) * 18,
110 g_quad_vertex_buffer_data,
111 GL_STATIC_DRAW);
112
113 // Load NTSC artifacts
114 int atdw, atdh;
115 unsigned char* artifactsData =
116 stbi_load("res/artifacts.bmp", &atdw, &atdh, 0, 3);
117
118 flipImageData(artifactsData, atdw, atdh, 3);
119
120 glBindTexture(GL_TEXTURE_2D, artifactsTex_.getId());
121 glTexImage2D(
122 GL_TEXTURE_2D,
123 0,
124 GL_RGB,
125 atdw,
126 atdh,
127 0,
128 GL_RGB,
129 GL_UNSIGNED_BYTE,
130 artifactsData);
131
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
133 glTexParameteri(
134 GL_TEXTURE_2D,
135 GL_TEXTURE_MIN_FILTER,
136 GL_NEAREST_MIPMAP_NEAREST);
137
138 glGenerateMipmap(GL_TEXTURE_2D);
139 stbi_image_free(artifactsData);
140
141 // Load NTSC scanlines
142 unsigned char* scanlinesData =
143 stbi_load("res/scanlines_333.bmp", &atdw, &atdh, 0, 3);
144
145 flipImageData(scanlinesData, atdw, atdh, 3);
146
147 glBindTexture(GL_TEXTURE_2D, scanlinesTex_.getId());
148 glTexImage2D(
149 GL_TEXTURE_2D,
150 0,
151 GL_RGB,
152 atdw,
153 atdh,
154 0,
155 GL_RGB,
156 GL_UNSIGNED_BYTE,
157 scanlinesData);
158
159 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
160 glTexParameteri(
161 GL_TEXTURE_2D,
162 GL_TEXTURE_MIN_FILTER,
163 GL_NEAREST_MIPMAP_NEAREST);
164
165 glGenerateMipmap(GL_TEXTURE_2D);
166 stbi_image_free(scanlinesData);
167}
168
169void Renderer::initializeFramebuffers()
170{
171 // Set up the framebuffer
172 glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId());
173 GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
174 glDrawBuffers(1, DrawBuffers);
175
176 // Set up the bloom framebuffer and depthbuffer
177 glBindFramebuffer(GL_FRAMEBUFFER, bloomFb_.getId());
178 GLenum DrawBuffers2[1] = {GL_COLOR_ATTACHMENT1};
179 glDrawBuffers(1, DrawBuffers2);
180
181 glBindRenderbuffer(GL_RENDERBUFFER, bloomDepth_.getId());
182 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width_, height_);
183 glFramebufferRenderbuffer(
184 GL_FRAMEBUFFER,
185 GL_DEPTH_ATTACHMENT,
186 GL_RENDERBUFFER,
187 bloomDepth_.getId());
188
189 // Set up the NTSC rendering buffers
190 glBindTexture(GL_TEXTURE_2D, renderPages_[0].getId());
191 glTexImage2D(
192 GL_TEXTURE_2D,
193 0,
194 GL_RGB,
195 GAME_WIDTH,
196 GAME_HEIGHT,
197 0,
198 GL_RGB,
199 GL_UNSIGNED_BYTE,
200 0);
201
202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
206
207 glBindTexture(GL_TEXTURE_2D, renderPages_[1].getId());
208 glTexImage2D(
209 GL_TEXTURE_2D,
210 0,
211 GL_RGB,
212 GAME_WIDTH,
213 GAME_HEIGHT,
214 0,
215 GL_RGB,
216 GL_UNSIGNED_BYTE,
217 0);
218
219 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
220 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
221 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
222 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
223
224 // Set up bloom rendering buffers
225 glBindTexture(GL_TEXTURE_2D, preBloomTex_.getId());
226 glTexImage2D(
227 GL_TEXTURE_2D,
228 0,
229 GL_RGB,
230 width_,
231 height_,
232 0,
233 GL_RGB,
234 GL_UNSIGNED_BYTE,
235 0);
236
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
241
242 glBindTexture(GL_TEXTURE_2D, bloomPassTex1_.getId());
243 glTexImage2D(
244 GL_TEXTURE_2D,
245 0,
246 GL_RGB,
247 width_ / 4,
248 height_ / 4,
249 0,
250 GL_RGB,
251 GL_UNSIGNED_BYTE,
252 0);
253
254 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
258
259 glBindTexture(GL_TEXTURE_2D, bloomPassTex2_.getId());
260 glTexImage2D(
261 GL_TEXTURE_2D,
262 0,
263 GL_RGB,
264 width_ / 4,
265 height_ / 4,
266 0,
267 GL_RGB,
268 GL_UNSIGNED_BYTE,
269 0);
270
271 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
272 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
273 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
274 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
275}
276
277Renderer::~Renderer()
278{
279 singletonInitialized_ = false;
280}
281
282void Renderer::fill(Texture& tex, Rectangle dstrect, int r, int g, int b)
283{
284 // Target the framebuffer
285 glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId());
286 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex.getId(), 0);
287
288 // Set up the vertex attributes
289 int width = tex.getWidth();
290 int height = tex.getHeight();
291
292 GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0;
293 GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0);
294 GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0;
295 GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0);
296
297 GLfloat vertexData[] = {
298 minx, miny,
299 maxx, miny,
300 maxx, maxy,
301 minx, miny,
302 minx, maxy,
303 maxx, maxy
304 };
305
306 GLBuffer vertexBuffer;
307 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId());
308 glBufferData(
309 GL_ARRAY_BUFFER,
310 sizeof(GLfloat) * 12,
311 vertexData, GL_STATIC_DRAW);
312
313 glEnableVertexAttribArray(0);
314 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
315
316 glViewport(0, 0, tex.getWidth(), tex.getHeight());
317 glClear(GL_DEPTH_BUFFER_BIT);
318
319 fillShader_.use();
320 glUniform3f(
321 fillShader_.getUniformLocation("vecColor"),
322 r / 255.0,
323 g / 255.0,
324 b / 255.0);
325
326 glDrawArrays(GL_TRIANGLES, 0, 6);
327
328 glDisableVertexAttribArray(0);
329}
330
331void Renderer::blit(
332 const Texture& src,
333 Texture& dst,
334 Rectangle srcrect,
335 Rectangle dstrect,
336 double alpha)
337{
338 alpha = glm::clamp(alpha, 0.0, 1.0);
339
340 // Target the framebuffer
341 glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId());
342 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dst.getId(), 0);
343
344 // Set up the vertex attributes
345 int width = dst.getWidth();
346 int height = dst.getHeight();
347
348 GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0;
349 GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0);
350 GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0;
351 GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0);
352
353 GLfloat vertexData[] = {
354 minx, miny,
355 maxx, miny,
356 minx, maxy,
357 maxx, maxy
358 };
359
360 GLBuffer vertexBuffer;
361 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId());
362 glBufferData(
363 GL_ARRAY_BUFFER,
364 sizeof(GLfloat) * 8,
365 vertexData,
366 GL_STATIC_DRAW);
367
368 glEnableVertexAttribArray(0);
369 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
370
371 GLfloat minu = (GLfloat) srcrect.x / src.getWidth();
372 GLfloat minv = 1 - ((GLfloat) srcrect.y / src.getHeight());
373 GLfloat maxu = (GLfloat) (srcrect.x + srcrect.w) / src.getWidth();
374 GLfloat maxv = 1 - ((GLfloat) (srcrect.y + srcrect.h) / src.getHeight());
375
376 GLfloat uvData[] = {
377 minu, minv,
378 maxu, minv,
379 minu, maxv,
380 maxu, maxv
381 };
382
383 GLBuffer uvBuffer;
384 glBindBuffer(GL_ARRAY_BUFFER, uvBuffer.getId());
385 glBufferData(
386 GL_ARRAY_BUFFER,
387 sizeof(GLfloat) * 8,
388 uvData,
389 GL_STATIC_DRAW);
390
391 glEnableVertexAttribArray(1);
392 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
393
394 // Set up the shader
395 blitShader_.use();
396 glClear(GL_DEPTH_BUFFER_BIT);
397 glViewport(0, 0, dst.getWidth(), dst.getHeight());
398
399 glActiveTexture(GL_TEXTURE0);
400 glBindTexture(GL_TEXTURE_2D, src.getId());
401 glUniform1i(blitShader_.getUniformLocation("srctex"), 0);
402 glUniform1f(blitShader_.getUniformLocation("alpha"), alpha);
403
404 // Blit!
405 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
406
407 // Unload everything
408 glDisableVertexAttribArray(1);
409 glDisableVertexAttribArray(0);
410}
411
412void Renderer::bloomPass1(
413 const GLTexture& src,
414 GLTexture& dst,
415 bool horizontal,
416 glm::vec2 srcRes,
417 glm::vec2 dstRes)
418{
419 glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId());
420 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dst.getId(), 0);
421 glViewport(0,0,dstRes.x,dstRes.y);
422 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
423 bloom1Shader_.use();
424
425 glActiveTexture(GL_TEXTURE0);
426 glBindTexture(GL_TEXTURE_2D, src.getId());
427 glUniform1i(bloom1Shader_.getUniformLocation("inTex"), 0);
428
429 glm::vec2 offset = glm::vec2(0.0);
430 if (horizontal)
431 {
432 offset.x = 1.2/srcRes.x;
433 } else {
434 offset.y = 1.2/srcRes.y;
435 }
436
437 glUniform2f(bloom1Shader_.getUniformLocation("offset"), offset.x, offset.y);
438
439 glEnableVertexAttribArray(0);
440 glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId());
441 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
442 glDrawArrays(GL_TRIANGLES, 0, 6);
443 glDisableVertexAttribArray(0);
444}
445
446void Renderer::renderScreen(const Texture& tex)
447{
448 // First we're going to composite our frame with the previous frame
449 // We start by setting up the framebuffer
450 glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId());
451 glFramebufferTexture(
452 GL_FRAMEBUFFER,
453 GL_COLOR_ATTACHMENT0,
454 renderPages_[curBuf_].getId(),
455 0);
456
457 // Set up the shader
458 glViewport(0,0,GAME_WIDTH,GAME_HEIGHT);
459 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
460 ntscShader_.use();
461
462 // Use the current frame texture, nearest neighbor and clamped to edge
463 glActiveTexture(GL_TEXTURE0);
464 glBindTexture(GL_TEXTURE_2D, tex.getId());
465 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
466 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
467 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
468 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
469 glUniform1i(ntscShader_.getUniformLocation("curFrameSampler"), 0);
470
471 // Use the previous frame composite texture, nearest neighbor and clamped to
472 // edge
473 glActiveTexture(GL_TEXTURE1);
474 glBindTexture(GL_TEXTURE_2D, renderPages_[(curBuf_ + 1) % 2].getId());
475 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
476 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
477 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
478 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
479 glUniform1i(ntscShader_.getUniformLocation("prevFrameSampler"), 1);
480
481 // Load the NTSC artifact texture
482 glActiveTexture(GL_TEXTURE2);
483 glBindTexture(GL_TEXTURE_2D, artifactsTex_.getId());
484 glUniform1i(ntscShader_.getUniformLocation("NTSCArtifactSampler"), 2);
485 glUniform1f(ntscShader_.getUniformLocation("NTSCLerp"), curBuf_ * 1.0);
486
487 // Change the 0.0 to a 1.0 or a 10.0 for a glitchy effect!
488 glUniform1f(ntscShader_.getUniformLocation("Tuning_NTSC"), 0.0);
489
490 // Render our composition
491 glEnableVertexAttribArray(0);
492 glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId());
493 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
494 glDrawArrays(GL_TRIANGLES, 0, 6);
495 glDisableVertexAttribArray(0);
496
497 // We're going to render the screen now
498 glBindFramebuffer(GL_FRAMEBUFFER, bloomFb_.getId());
499 glFramebufferTexture(
500 GL_FRAMEBUFFER,
501 GL_COLOR_ATTACHMENT1,
502 preBloomTex_.getId(),
503 0);
504
505 glViewport(0,0,width_,height_);
506 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
507 finalShader_.use();
508
509 // Use the composited frame texture, linearly filtered and filling in black
510 // for the border
511 glActiveTexture(GL_TEXTURE0);
512 glBindTexture(GL_TEXTURE_2D, renderPages_[curBuf_].getId());
513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
514 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
515 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
516 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
517
518 float borderColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
519 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
520
521 glGenerateMipmap(GL_TEXTURE_2D);
522 glUniform1i(finalShader_.getUniformLocation("rendertex"), 0);
523
524 // Use the scanlines texture
525 glActiveTexture(GL_TEXTURE1);
526 glBindTexture(GL_TEXTURE_2D, scanlinesTex_.getId());
527 glUniform1i(finalShader_.getUniformLocation("scanlinestex"), 1);
528
529 // Initialize the MVP matrices
530 glm::mat4 p_matrix = glm::perspective(
531 glm::radians(25.0f),
532 static_cast<float>(width_) / static_cast<float>(height_),
533 0.1f,
534 100.0f);
535
536 glm::mat4 v_matrix = glm::lookAt(
537 glm::vec3(3.75,0,0), // Camera
538 glm::vec3(0,0,0), // Center
539 glm::vec3(0,1,0)); // Up
540
541 glm::mat4 m_matrix = glm::mat4(1.0);
542 glm::mat4 mvp_matrix = p_matrix * v_matrix * m_matrix;
543
544 glUniformMatrix4fv(
545 finalShader_.getUniformLocation("MVP"),
546 1,
547 GL_FALSE,
548 &mvp_matrix[0][0]);
549
550 glUniformMatrix4fv(
551 finalShader_.getUniformLocation("worldMat"),
552 1,
553 GL_FALSE,
554 &m_matrix[0][0]);
555
556 glUniform2f(finalShader_.getUniformLocation("resolution"), width_, height_);
557 glUniform1f(finalShader_.getUniformLocation("iGlobalTime"), glfwGetTime());
558
559 glUniform3f(
560 finalShader_.getUniformLocation("frameColor"),
561 0.76f,
562 0.78f,
563 0.81f);
564
565 glEnableVertexAttribArray(0);
566 glBindBuffer(GL_ARRAY_BUFFER, monitor_.getVertexBufferId());
567 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
568
569 glEnableVertexAttribArray(1);
570 glBindBuffer(GL_ARRAY_BUFFER, monitor_.getNormalBufferId());
571 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
572
573 glEnableVertexAttribArray(2);
574 glBindBuffer(GL_ARRAY_BUFFER, monitor_.getUvBufferId());
575 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
576
577 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor_.getIndexBufferId());
578 glDrawElements(
579 GL_TRIANGLES,
580 monitor_.getIndexCount(),
581 GL_UNSIGNED_SHORT,
582 nullptr);
583
584 glDisableVertexAttribArray(2);
585 glDisableVertexAttribArray(1);
586 glDisableVertexAttribArray(0);
587
588 // First pass of bloom!
589 glm::vec2 bufferSize = glm::vec2(width_, height_);
590
591 bloomPass1(
592 preBloomTex_,
593 bloomPassTex1_,
594 true,
595 bufferSize,
596 bufferSize / 4.0f);
597
598 bloomPass1(
599 bloomPassTex1_,
600 bloomPassTex2_,
601 false,
602 bufferSize / 4.0f,
603 bufferSize / 4.0f);
604
605 // Do the second pass of bloom and render to screen
606 glBindFramebuffer(GL_FRAMEBUFFER, 0);
607 glViewport(0, 0, width_, height_);
608 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
609 bloom2Shader_.use();
610
611 glActiveTexture(GL_TEXTURE0);
612 glBindTexture(GL_TEXTURE_2D, preBloomTex_.getId());
613 glUniform1i(bloom2Shader_.getUniformLocation("clearTex"), 0);
614
615 glActiveTexture(GL_TEXTURE1);
616 glBindTexture(GL_TEXTURE_2D, bloomPassTex2_.getId());
617 glUniform1i(bloom2Shader_.getUniformLocation("blurTex"), 1);
618
619 glUniform1f(bloom2Shader_.getUniformLocation("iGlobalTime"), glfwGetTime());
620
621 glEnableVertexAttribArray(0);
622 glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId());
623 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
624 glDrawArrays(GL_TRIANGLES, 0, 6);
625 glDisableVertexAttribArray(0);
626
627 glfwSwapBuffers(window_.getHandle());
628
629 curBuf_ = (curBuf_ + 1) % 2;
630}
diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h new file mode 100644 index 0000000..0b10af5 --- /dev/null +++ b/src/renderer/renderer.h
@@ -0,0 +1,114 @@
1#ifndef RENDERER_H
2#define RENDERER_H
3
4#include "gl.h"
5#include "wrappers.h"
6#include "mesh.h"
7#include "shader.h"
8#include <glm/glm.hpp>
9
10class Texture;
11struct Rectangle;
12
13class Renderer {
14public:
15
16 class Window {
17 public:
18
19 Window();
20
21 Window(const Window& other) = delete;
22 Window& operator=(const Window& other) = delete;
23
24 ~Window();
25
26 inline GLFWwindow* getHandle()
27 {
28 return window_;
29 }
30
31 private:
32
33 GLFWwindow* window_;
34 };
35
36 static inline bool isSingletonInitialized()
37 {
38 return singletonInitialized_;
39 }
40
41 Renderer();
42
43 Renderer(const Renderer& other) = delete;
44 Renderer& operator=(const Renderer& other) = delete;
45
46 ~Renderer();
47
48 inline Window& getWindow()
49 {
50 return window_;
51 }
52
53 void fill(
54 Texture& tex,
55 Rectangle loc,
56 int r,
57 int g,
58 int b);
59
60 void blit(
61 const Texture& src,
62 Texture& dst,
63 Rectangle srcrect,
64 Rectangle dstrect,
65 double alpha = 1.0);
66
67 void renderScreen(const Texture& tex);
68
69private:
70
71 friend void setFramebufferSize(GLFWwindow* w, int width, int height);
72
73 void initializeFramebuffers();
74
75 void bloomPass1(
76 const GLTexture& src,
77 GLTexture& dst,
78 bool horizontal,
79 glm::vec2 srcRes,
80 glm::vec2 dstRes);
81
82 static bool singletonInitialized_;
83
84 Window window_;
85 GLVertexArray vao_;
86
87 GLFramebuffer genericFb_;
88 GLFramebuffer bloomFb_;
89 GLRenderbuffer bloomDepth_;
90
91 GLTexture renderPages_[2];
92 GLTexture preBloomTex_;
93 GLTexture bloomPassTex1_;
94 GLTexture bloomPassTex2_;
95
96 Mesh monitor_;
97 GLBuffer quadBuffer_;
98
99 GLTexture artifactsTex_;
100 GLTexture scanlinesTex_;
101
102 Shader ntscShader_;
103 Shader finalShader_;
104 Shader blitShader_;
105 Shader fillShader_;
106 Shader bloom1Shader_;
107 Shader bloom2Shader_;
108
109 size_t curBuf_ = 0;
110 int width_;
111 int height_;
112};
113
114#endif
diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp new file mode 100644 index 0000000..735fc22 --- /dev/null +++ b/src/renderer/shader.cpp
@@ -0,0 +1,84 @@
1#include "shader.h"
2#include <fstream>
3#include <vector>
4#include "util.h"
5
6Shader::Shader(std::string name)
7{
8 GLShader vertexShader(GL_VERTEX_SHADER);
9 GLShader fragmentShader(GL_FRAGMENT_SHADER);
10
11 std::ifstream vertexFile("shaders/" + name + ".vertex");
12 std::ifstream fragmentFile("shaders/" + name + ".fragment");
13
14 std::string vertexCode(slurp(vertexFile));
15 std::string fragmentCode(slurp(fragmentFile));
16
17 const char* vertexCodePtr = vertexCode.c_str();
18 const char* fragmentCodePtr = fragmentCode.c_str();
19
20 glShaderSource(vertexShader.getId(), 1, &vertexCodePtr, nullptr);
21 glShaderSource(fragmentShader.getId(), 1, &fragmentCodePtr, nullptr);
22
23 glCompileShader(vertexShader.getId());
24 glCompileShader(fragmentShader.getId());
25
26#ifdef DEBUG
27 GLint result = GL_FALSE;
28 int infoLogLength;
29
30 glGetShaderiv(vertexShader.getId(), GL_COMPILE_STATUS, &result);
31
32 if (result == GL_FALSE)
33 {
34 glGetShaderiv(vertexShader.getId(), GL_INFO_LOG_LENGTH, &infoLogLength);
35
36 std::vector<char> errMsg(infoLogLength);
37 glGetShaderInfoLog(
38 vertexShader.getId(),
39 infoLogLength,
40 nullptr,
41 errMsg.data());
42
43 throw std::gl_error("Could not compile shader", errMsg.data());
44 }
45
46 glGetShaderiv(fragmentShader.getId(), GL_COMPILE_STATUS, &result);
47
48 if (result == GL_FALSE)
49 {
50 glGetShaderiv(fragmentShader.getId(), GL_INFO_LOG_LENGTH, &infoLogLength);
51
52 std::vector<char> errMsg(infoLogLength);
53 glGetShaderInfoLog(
54 fragmentShader.getId(),
55 infoLogLength,
56 nullptr,
57 errMsg.data());
58
59 throw std::gl_error("Could not compile shader", errMsg.data());
60 }
61#endif
62
63 glAttachShader(program_.getId(), vertexShader.getId());
64 glAttachShader(program_.getId(), fragmentShader.getId());
65 glLinkProgram(program_.getId());
66
67#ifdef DEBUG
68 glGetProgramiv(program_.getId(), GL_LINK_STATUS, &result);
69
70 if (result == GL_FALSE)
71 {
72 glGetProgramiv(program_.getId(), GL_INFO_LOG_LENGTH, &infoLogLength);
73
74 std::vector<char> errMsg(infoLogLength);
75 glGetProgramInfoLog(
76 program_.getId(),
77 infoLogLength,
78 nullptr,
79 errMsg.data());
80
81 throw std::gl_error("Could not link shader program", errMsg.data());
82 }
83#endif
84}
diff --git a/src/renderer/shader.h b/src/renderer/shader.h new file mode 100644 index 0000000..d2c673c --- /dev/null +++ b/src/renderer/shader.h
@@ -0,0 +1,58 @@
1#ifndef SHADER_H_25115B63
2#define SHADER_H_25115B63
3
4#include <string>
5#include <stdexcept>
6#include "gl.h"
7#include "wrappers.h"
8
9class gl_error : public std::logic_error {
10public:
11
12 gl_error(
13 const char* msg,
14 std::string info) :
15 std::logic_error(msg),
16 info_(std::move(info))
17 {
18 }
19
20 gl_error(
21 std::string& msg,
22 std::string info) :
23 std::logic_error(msg),
24 info_(std::move(info))
25 {
26 }
27
28 inline const std::string& getInfo() const
29 {
30 return info_;
31 }
32
33private:
34
35 std::string info_;
36};
37
38class Shader {
39public:
40
41 Shader(std::string name);
42
43 inline void use()
44 {
45 glUseProgram(program_.getId());
46 }
47
48 inline GLint getUniformLocation(const GLchar* name)
49 {
50 return glGetUniformLocation(program_.getId(), name);
51 }
52
53private:
54
55 GLProgram program_;
56};
57
58#endif /* end of include guard: SHADER_H_25115B63 */
diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp new file mode 100644 index 0000000..04d5cf4 --- /dev/null +++ b/src/renderer/texture.cpp
@@ -0,0 +1,120 @@
1#include "texture.h"
2#include <stdexcept>
3#include <stb_image.h>
4#include "renderer.h"
5#include "util.h"
6
7Texture::Texture(
8 int width,
9 int height) :
10 width_(width),
11 height_(height)
12{
13 if (!Renderer::isSingletonInitialized())
14 {
15 throw std::logic_error("Renderer needs to be initialized");
16 }
17
18 glBindTexture(GL_TEXTURE_2D, texture_.getId());
19 glTexImage2D(
20 GL_TEXTURE_2D,
21 0,
22 GL_RGBA,
23 width_,
24 height_,
25 0,
26 GL_RGBA,
27 GL_UNSIGNED_BYTE,
28 0);
29
30 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
31 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
32 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
33 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
34}
35
36Texture::Texture(const char* filename)
37{
38 if (!Renderer::isSingletonInitialized())
39 {
40 throw std::logic_error("Renderer needs to be initialized");
41 }
42
43 glBindTexture(GL_TEXTURE_2D, texture_.getId());
44 unsigned char* data = stbi_load(filename, &width_, &height_, 0, 4);
45 flipImageData(data, width_, height_, 4);
46 glTexImage2D(
47 GL_TEXTURE_2D,
48 0,
49 GL_RGBA,
50 width_,
51 height_,
52 0,
53 GL_RGBA,
54 GL_UNSIGNED_BYTE,
55 data);
56
57 stbi_image_free(data);
58 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
59 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
60 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
61 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
62}
63
64Texture::Texture(
65 const Texture& tex) :
66 width_(tex.width_),
67 height_(tex.height_)
68{
69 if (!Renderer::isSingletonInitialized())
70 {
71 throw std::logic_error("Renderer needs to be initialized");
72 }
73
74 unsigned char* data = new unsigned char[4 * width_ * height_];
75 glBindTexture(GL_TEXTURE_2D, tex.getId());
76 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
77
78 glBindTexture(GL_TEXTURE_2D, texture_.getId());
79 glTexImage2D(
80 GL_TEXTURE_2D,
81 0,
82 GL_RGBA,
83 width_,
84 height_,
85 0,
86 GL_RGBA,
87 GL_UNSIGNED_BYTE,
88 data);
89
90 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
91 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
92 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
93 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
94
95 delete[] data;
96}
97
98Texture::Texture(Texture&& tex) : Texture(0, 0)
99{
100 swap(*this, tex);
101}
102
103Texture& Texture::operator= (Texture tex)
104{
105 swap(*this, tex);
106
107 return *this;
108}
109
110void swap(Texture& tex1, Texture& tex2)
111{
112 std::swap(tex1.width_, tex2.width_);
113 std::swap(tex1.height_, tex2.height_);
114 std::swap(tex1.texture_, tex2.texture_);
115}
116
117Rectangle Texture::entirety() const
118{
119 return {0, 0, width_, height_};
120}
diff --git a/src/renderer/texture.h b/src/renderer/texture.h new file mode 100644 index 0000000..3aa8773 --- /dev/null +++ b/src/renderer/texture.h
@@ -0,0 +1,52 @@
1#ifndef TEXTURE_H_84EC6DF6
2#define TEXTURE_H_84EC6DF6
3
4#include "wrappers.h"
5
6struct Rectangle {
7 int x;
8 int y;
9 int w;
10 int h;
11};
12
13class Texture {
14public:
15
16 Texture(int width, int height);
17
18 Texture(const char* file);
19
20 Texture(const Texture& tex);
21
22 Texture(Texture&& tex);
23
24 Texture& operator= (Texture tex);
25
26 friend void swap(Texture& tex1, Texture& tex2);
27
28 Rectangle entirety() const;
29
30 inline GLuint getId() const
31 {
32 return texture_.getId();
33 }
34
35 inline int getWidth() const
36 {
37 return width_;
38 }
39
40 inline int getHeight() const
41 {
42 return height_;
43 }
44
45private:
46
47 GLTexture texture_;
48 int width_;
49 int height_;
50};
51
52#endif /* end of include guard: TEXTURE_H_84EC6DF6 */
diff --git a/src/renderer/wrappers.h b/src/renderer/wrappers.h new file mode 100644 index 0000000..c6edc11 --- /dev/null +++ b/src/renderer/wrappers.h
@@ -0,0 +1,273 @@
1#ifndef WRAPPERS_H_1EE0965B
2#define WRAPPERS_H_1EE0965B
3
4#include "gl.h"
5#include <utility>
6
7class GLVertexArray {
8public:
9
10 GLVertexArray()
11 {
12 glGenVertexArrays(1, &id_);
13 }
14
15 GLVertexArray(const GLVertexArray& other) = delete;
16 GLVertexArray& operator=(const GLVertexArray& other) = delete;
17
18 GLVertexArray(GLVertexArray&& other) : GLVertexArray()
19 {
20 std::swap(id_, other.id_);
21 }
22
23 GLVertexArray& operator=(GLVertexArray&& other)
24 {
25 std::swap(id_, other.id_);
26
27 return *this;
28 }
29
30 ~GLVertexArray()
31 {
32 glDeleteVertexArrays(1, &id_);
33 }
34
35 inline GLuint getId() const
36 {
37 return id_;
38 }
39
40private:
41
42 GLuint id_;
43};
44
45class GLFramebuffer {
46public:
47
48 GLFramebuffer()
49 {
50 glGenFramebuffers(1, &id_);
51 }
52
53 GLFramebuffer(const GLFramebuffer& other) = delete;
54 GLFramebuffer& operator=(const GLFramebuffer& other) = delete;
55
56 GLFramebuffer(GLFramebuffer&& other) : GLFramebuffer()
57 {
58 std::swap(id_, other.id_);
59 }
60
61 GLFramebuffer& operator=(GLFramebuffer&& other)
62 {
63 std::swap(id_, other.id_);
64
65 return *this;
66 }
67
68 ~GLFramebuffer()
69 {
70 glDeleteFramebuffers(1, &id_);
71 }
72
73 inline GLuint getId() const
74 {
75 return id_;
76 }
77
78private:
79
80 GLuint id_;
81};
82
83class GLRenderbuffer {
84public:
85
86 GLRenderbuffer()
87 {
88 glGenRenderbuffers(1, &id_);
89 }
90
91 GLRenderbuffer(const GLRenderbuffer& other) = delete;
92 GLRenderbuffer& operator=(const GLRenderbuffer& other) = delete;
93
94 GLRenderbuffer(GLRenderbuffer&& other) : GLRenderbuffer()
95 {
96 std::swap(id_, other.id_);
97 }
98
99 GLRenderbuffer& operator=(GLRenderbuffer&& other)
100 {
101 std::swap(id_, other.id_);
102
103 return *this;
104 }
105
106 ~GLRenderbuffer()
107 {
108 glDeleteRenderbuffers(1, &id_);
109 }
110
111 inline GLuint getId() const
112 {
113 return id_;
114 }
115
116private:
117
118 GLuint id_;
119};
120
121class GLBuffer {
122public:
123
124 GLBuffer()
125 {
126 glGenBuffers(1, &id_);
127 }
128
129 GLBuffer(const GLBuffer& other) = delete;
130 GLBuffer& operator=(const GLBuffer& other) = delete;
131
132 GLBuffer(GLBuffer&& other) : GLBuffer()
133 {
134 std::swap(id_, other.id_);
135 }
136
137 GLBuffer& operator=(GLBuffer&& other)
138 {
139 std::swap(id_, other.id_);
140
141 return *this;
142 }
143
144 ~GLBuffer()
145 {
146 glDeleteBuffers(1, &id_);
147 }
148
149 inline GLuint getId() const
150 {
151 return id_;
152 }
153
154private:
155
156 GLuint id_;
157};
158
159class GLTexture {
160public:
161
162 GLTexture()
163 {
164 glGenTextures(1, &id_);
165 }
166
167 GLTexture(const GLTexture& other) = delete;
168 GLTexture& operator=(const GLTexture& other) = delete;
169
170 GLTexture(GLTexture&& other) : GLTexture()
171 {
172 std::swap(id_, other.id_);
173 }
174
175 GLTexture& operator=(GLTexture&& other)
176 {
177 std::swap(id_, other.id_);
178
179 return *this;
180 }
181
182 ~GLTexture()
183 {
184 glDeleteTextures(1, &id_);
185 }
186
187 inline GLuint getId() const
188 {
189 return id_;
190 }
191
192private:
193
194 GLuint id_;
195};
196
197class GLShader {
198public:
199
200 GLShader(GLenum type)
201 {
202 id_ = glCreateShader(type);
203 }
204
205 GLShader(const GLShader& other) = delete;
206 GLShader& operator=(const GLShader& other) = delete;
207
208 GLShader(GLShader&& other) : GLShader(GL_VERTEX_SHADER)
209 {
210 std::swap(id_, other.id_);
211 }
212
213 GLShader& operator=(GLShader&& other)
214 {
215 std::swap(id_, other.id_);
216
217 return *this;
218 }
219
220 ~GLShader()
221 {
222 glDeleteShader(id_);
223 }
224
225 inline GLuint getId() const
226 {
227 return id_;
228 }
229
230private:
231
232 GLuint id_;
233};
234
235class GLProgram {
236public:
237
238 GLProgram()
239 {
240 id_ = glCreateProgram();
241 }
242
243 GLProgram(const GLProgram& other) = delete;
244 GLProgram& operator=(const GLProgram& other) = delete;
245
246 GLProgram(GLProgram&& other) : GLProgram()
247 {
248 std::swap(id_, other.id_);
249 }
250
251 GLProgram& operator=(GLProgram&& other)
252 {
253 std::swap(id_, other.id_);
254
255 return *this;
256 }
257
258 ~GLProgram()
259 {
260 glDeleteProgram(id_);
261 }
262
263 inline GLuint getId() const
264 {
265 return id_;
266 }
267
268private:
269
270 GLuint id_;
271};
272
273#endif /* end of include guard: WRAPPERS_H_1EE0965B */
diff --git a/src/stb_image.h b/src/stb_image.h deleted file mode 100644 index cea66f5..0000000 --- a/src/stb_image.h +++ /dev/null
@@ -1,6326 +0,0 @@
1/* stb_image - v2.02 - public domain image loader - http://nothings.org/stb_image.h
2 no warranty implied; use at your own risk
3
4 Do this:
5 #define STB_IMAGE_IMPLEMENTATION
6 before you include this file in *one* C or C++ file to create the implementation.
7
8 // i.e. it should look like this:
9 #include ...
10 #include ...
11 #include ...
12 #define STB_IMAGE_IMPLEMENTATION
13 #include "stb_image.h"
14
15 You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
16 And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
17
18
19 QUICK NOTES:
20 Primarily of interest to game developers and other people who can
21 avoid problematic images and only need the trivial interface
22
23 JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
24 PNG 1/2/4/8-bit-per-channel (16 bpc not supported)
25
26 TGA (not sure what subset, if a subset)
27 BMP non-1bpp, non-RLE
28 PSD (composited view only, no extra channels)
29
30 GIF (*comp always reports as 4-channel)
31 HDR (radiance rgbE format)
32 PIC (Softimage PIC)
33 PNM (PPM and PGM binary only)
34
35 - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
36 - decode from arbitrary I/O callbacks
37 - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
38
39 Full documentation under "DOCUMENTATION" below.
40
41
42 Revision 2.00 release notes:
43
44 - Progressive JPEG is now supported.
45
46 - PPM and PGM binary formats are now supported, thanks to Ken Miller.
47
48 - x86 platforms now make use of SSE2 SIMD instructions for
49 JPEG decoding, and ARM platforms can use NEON SIMD if requested.
50 This work was done by Fabian "ryg" Giesen. SSE2 is used by
51 default, but NEON must be enabled explicitly; see docs.
52
53 With other JPEG optimizations included in this version, we see
54 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup
55 on a JPEG on an ARM machine, relative to previous versions of this
56 library. The same results will not obtain for all JPGs and for all
57 x86/ARM machines. (Note that progressive JPEGs are significantly
58 slower to decode than regular JPEGs.) This doesn't mean that this
59 is the fastest JPEG decoder in the land; rather, it brings it
60 closer to parity with standard libraries. If you want the fastest
61 decode, look elsewhere. (See "Philosophy" section of docs below.)
62
63 See final bullet items below for more info on SIMD.
64
65 - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing
66 the memory allocator. Unlike other STBI libraries, these macros don't
67 support a context parameter, so if you need to pass a context in to
68 the allocator, you'll have to store it in a global or a thread-local
69 variable.
70
71 - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and
72 STBI_NO_LINEAR.
73 STBI_NO_HDR: suppress implementation of .hdr reader format
74 STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API
75
76 - You can suppress implementation of any of the decoders to reduce
77 your code footprint by #defining one or more of the following
78 symbols before creating the implementation.
79
80 STBI_NO_JPEG
81 STBI_NO_PNG
82 STBI_NO_BMP
83 STBI_NO_PSD
84 STBI_NO_TGA
85 STBI_NO_GIF
86 STBI_NO_HDR
87 STBI_NO_PIC
88 STBI_NO_PNM (.ppm and .pgm)
89
90 - You can request *only* certain decoders and suppress all other ones
91 (this will be more forward-compatible, as addition of new decoders
92 doesn't require you to disable them explicitly):
93
94 STBI_ONLY_JPEG
95 STBI_ONLY_PNG
96 STBI_ONLY_BMP
97 STBI_ONLY_PSD
98 STBI_ONLY_TGA
99 STBI_ONLY_GIF
100 STBI_ONLY_HDR
101 STBI_ONLY_PIC
102 STBI_ONLY_PNM (.ppm and .pgm)
103
104 Note that you can define multiples of these, and you will get all
105 of them ("only x" and "only y" is interpreted to mean "only x&y").
106
107 - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
108 want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
109
110 - Compilation of all SIMD code can be suppressed with
111 #define STBI_NO_SIMD
112 It should not be necessary to disable SIMD unless you have issues
113 compiling (e.g. using an x86 compiler which doesn't support SSE
114 intrinsics or that doesn't support the method used to detect
115 SSE2 support at run-time), and even those can be reported as
116 bugs so I can refine the built-in compile-time checking to be
117 smarter.
118
119 - The old STBI_SIMD system which allowed installing a user-defined
120 IDCT etc. has been removed. If you need this, don't upgrade. My
121 assumption is that almost nobody was doing this, and those who
122 were will find the built-in SIMD more satisfactory anyway.
123
124 - RGB values computed for JPEG images are slightly different from
125 previous versions of stb_image. (This is due to using less
126 integer precision in SIMD.) The C code has been adjusted so
127 that the same RGB values will be computed regardless of whether
128 SIMD support is available, so your app should always produce
129 consistent results. But these results are slightly different from
130 previous versions. (Specifically, about 3% of available YCbCr values
131 will compute different RGB results from pre-1.49 versions by +-1;
132 most of the deviating values are one smaller in the G channel.)
133
134 - If you must produce consistent results with previous versions of
135 stb_image, #define STBI_JPEG_OLD and you will get the same results
136 you used to; however, you will not get the SIMD speedups for
137 the YCbCr-to-RGB conversion step (although you should still see
138 significant JPEG speedup from the other changes).
139
140 Please note that STBI_JPEG_OLD is a temporary feature; it will be
141 removed in future versions of the library. It is only intended for
142 near-term back-compatibility use.
143
144
145 Latest revision history:
146 2.02 (2015-01-19) fix incorrect assert, fix warning
147 2.01 (2015-01-17) fix various warnings
148 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
149 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD
150 progressive JPEG
151 PGM/PPM support
152 STBI_MALLOC,STBI_REALLOC,STBI_FREE
153 STBI_NO_*, STBI_ONLY_*
154 GIF bugfix
155 1.48 (2014-12-14) fix incorrectly-named assert()
156 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)
157 optimize PNG
158 fix bug in interlaced PNG with user-specified channel count
159 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG
160 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc
161
162 See end of file for full revision history.
163
164
165 ============================ Contributors =========================
166
167 Image formats Bug fixes & warning fixes
168 Sean Barrett (jpeg, png, bmp) Marc LeBlanc
169 Nicolas Schulz (hdr, psd) Christpher Lloyd
170 Jonathan Dummer (tga) Dave Moore
171 Jean-Marc Lienher (gif) Won Chun
172 Tom Seddon (pic) the Horde3D community
173 Thatcher Ulrich (psd) Janez Zemva
174 Ken Miller (pgm, ppm) Jonathan Blow
175 Laurent Gomila
176 Aruelien Pocheville
177 Extensions, features Ryamond Barbiero
178 Jetro Lauha (stbi_info) David Woo
179 Martin "SpartanJ" Golini (stbi_info) Martin Golini
180 James "moose2000" Brown (iPhone PNG) Roy Eltham
181 Ben "Disch" Wenger (io callbacks) Luke Graham
182 Omar Cornut (1/2/4-bit PNG) Thomas Ruf
183 John Bartholomew
184 Ken Hamada
185 Optimizations & bugfixes Cort Stratton
186 Fabian "ryg" Giesen Blazej Dariusz Roszkowski
187 Arseny Kapoulkine Thibault Reuille
188 Paul Du Bois
189 Guillaume George
190 If your name should be here but Jerry Jansson
191 isn't, let Sean know. Hayaki Saito
192 Johan Duparc
193 Ronny Chevalier
194 Michal Cichon
195 Tero Hanninen
196 Sergio Gonzalez
197 Cass Everitt
198 Engin Manap
199
200License:
201 This software is in the public domain. Where that dedication is not
202 recognized, you are granted a perpetual, irrevocable license to copy
203 and modify this file however you want.
204
205*/
206
207#ifndef STBI_INCLUDE_STB_IMAGE_H
208#define STBI_INCLUDE_STB_IMAGE_H
209
210// DOCUMENTATION
211//
212// Limitations:
213// - no 16-bit-per-channel PNG
214// - no 12-bit-per-channel JPEG
215// - no JPEGs with arithmetic coding
216// - no 1-bit BMP
217// - GIF always returns *comp=4
218//
219// Basic usage (see HDR discussion below for HDR usage):
220// int x,y,n;
221// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
222// // ... process data if not NULL ...
223// // ... x = width, y = height, n = # 8-bit components per pixel ...
224// // ... replace '0' with '1'..'4' to force that many components per pixel
225// // ... but 'n' will always be the number that it would have been if you said 0
226// stbi_image_free(data)
227//
228// Standard parameters:
229// int *x -- outputs image width in pixels
230// int *y -- outputs image height in pixels
231// int *comp -- outputs # of image components in image file
232// int req_comp -- if non-zero, # of image components requested in result
233//
234// The return value from an image loader is an 'unsigned char *' which points
235// to the pixel data, or NULL on an allocation failure or if the image is
236// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
237// with each pixel consisting of N interleaved 8-bit components; the first
238// pixel pointed to is top-left-most in the image. There is no padding between
239// image scanlines or between pixels, regardless of format. The number of
240// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
241// If req_comp is non-zero, *comp has the number of components that _would_
242// have been output otherwise. E.g. if you set req_comp to 4, you will always
243// get RGBA output, but you can check *comp to see if it's trivially opaque
244// because e.g. there were only 3 channels in the source image.
245//
246// An output image with N components has the following components interleaved
247// in this order in each pixel:
248//
249// N=#comp components
250// 1 grey
251// 2 grey, alpha
252// 3 red, green, blue
253// 4 red, green, blue, alpha
254//
255// If image loading fails for any reason, the return value will be NULL,
256// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
257// can be queried for an extremely brief, end-user unfriendly explanation
258// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
259// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
260// more user-friendly ones.
261//
262// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
263//
264// ===========================================================================
265//
266// Philosophy
267//
268// stb libraries are designed with the following priorities:
269//
270// 1. easy to use
271// 2. easy to maintain
272// 3. good performance
273//
274// Sometimes I let "good performance" creep up in priority over "easy to maintain",
275// and for best performance I may provide less-easy-to-use APIs that give higher
276// performance, in addition to the easy to use ones. Nevertheless, it's important
277// to keep in mind that from the standpoint of you, a client of this library,
278// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all.
279//
280// Some secondary priorities arise directly from the first two, some of which
281// make more explicit reasons why performance can't be emphasized.
282//
283// - Portable ("ease of use")
284// - Small footprint ("easy to maintain")
285// - No dependencies ("ease of use")
286//
287// ===========================================================================
288//
289// I/O callbacks
290//
291// I/O callbacks allow you to read from arbitrary sources, like packaged
292// files or some other source. Data read from callbacks are processed
293// through a small internal buffer (currently 128 bytes) to try to reduce
294// overhead.
295//
296// The three functions you must define are "read" (reads some bytes of data),
297// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
298//
299// ===========================================================================
300//
301// SIMD support
302//
303// The JPEG decoder will try to automatically use SIMD kernels on x86 when
304// supported by the compiler. For ARM Neon support, you must explicitly
305// request it.
306//
307// (The old do-it-yourself SIMD API is no longer supported in the current
308// code.)
309//
310// On x86, SSE2 will automatically be used when available based on a run-time
311// test; if not, the generic C versions are used as a fall-back. On ARM targets,
312// the typical path is to have separate builds for NEON and non-NEON devices
313// (at least this is true for iOS and Android). Therefore, the NEON support is
314// toggled by a build flag: define STBI_NEON to get NEON loops.
315//
316// The output of the JPEG decoder is slightly different from versions where
317// SIMD support was introduced (that is, for versions before 1.49). The
318// difference is only +-1 in the 8-bit RGB channels, and only on a small
319// fraction of pixels. You can force the pre-1.49 behavior by defining
320// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path
321// and hence cost some performance.
322//
323// If for some reason you do not want to use any of SIMD code, or if
324// you have issues compiling it, you can disable it entirely by
325// defining STBI_NO_SIMD.
326//
327// ===========================================================================
328//
329// HDR image support (disable by defining STBI_NO_HDR)
330//
331// stb_image now supports loading HDR images in general, and currently
332// the Radiance .HDR file format, although the support is provided
333// generically. You can still load any file through the existing interface;
334// if you attempt to load an HDR file, it will be automatically remapped to
335// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
336// both of these constants can be reconfigured through this interface:
337//
338// stbi_hdr_to_ldr_gamma(2.2f);
339// stbi_hdr_to_ldr_scale(1.0f);
340//
341// (note, do not use _inverse_ constants; stbi_image will invert them
342// appropriately).
343//
344// Additionally, there is a new, parallel interface for loading files as
345// (linear) floats to preserve the full dynamic range:
346//
347// float *data = stbi_loadf(filename, &x, &y, &n, 0);
348//
349// If you load LDR images through this interface, those images will
350// be promoted to floating point values, run through the inverse of
351// constants corresponding to the above:
352//
353// stbi_ldr_to_hdr_scale(1.0f);
354// stbi_ldr_to_hdr_gamma(2.2f);
355//
356// Finally, given a filename (or an open file or memory block--see header
357// file for details) containing image data, you can query for the "most
358// appropriate" interface to use (that is, whether the image is HDR or
359// not), using:
360//
361// stbi_is_hdr(char *filename);
362//
363// ===========================================================================
364//
365// iPhone PNG support:
366//
367// By default we convert iphone-formatted PNGs back to RGB, even though
368// they are internally encoded differently. You can disable this conversion
369// by by calling stbi_convert_iphone_png_to_rgb(0), in which case
370// you will always just get the native iphone "format" through (which
371// is BGR stored in RGB).
372//
373// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
374// pixel to remove any premultiplied alpha *only* if the image file explicitly
375// says there's premultiplied data (currently only happens in iPhone images,
376// and only if iPhone convert-to-rgb processing is on).
377//
378
379
380#ifndef STBI_NO_STDIO
381#include <stdio.h>
382#endif // STBI_NO_STDIO
383
384#define STBI_VERSION 1
385
386enum
387{
388 STBI_default = 0, // only used for req_comp
389
390 STBI_grey = 1,
391 STBI_grey_alpha = 2,
392 STBI_rgb = 3,
393 STBI_rgb_alpha = 4
394};
395
396typedef unsigned char stbi_uc;
397
398#ifdef __cplusplus
399extern "C" {
400#endif
401
402#ifdef STB_IMAGE_STATIC
403#define STBIDEF static
404#else
405#define STBIDEF extern
406#endif
407
408//////////////////////////////////////////////////////////////////////////////
409//
410// PRIMARY API - works on images of any type
411//
412
413//
414// load image by filename, open file, or memory buffer
415//
416
417typedef struct
418{
419 int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
420 void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
421 int (*eof) (void *user); // returns nonzero if we are at end of file/data
422} stbi_io_callbacks;
423
424STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
425STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp);
426STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp);
427
428#ifndef STBI_NO_STDIO
429STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
430// for stbi_load_from_file, file pointer is left pointing immediately after image
431#endif
432
433#ifndef STBI_NO_LINEAR
434 STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);
435 STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
436 STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp);
437
438 #ifndef STBI_NO_STDIO
439 STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);
440 #endif
441#endif
442
443#ifndef STBI_NO_HDR
444 STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
445 STBIDEF void stbi_hdr_to_ldr_scale(float scale);
446#endif
447
448#ifndef STBI_NO_LINEAR
449 STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
450 STBIDEF void stbi_ldr_to_hdr_scale(float scale);
451#endif // STBI_NO_HDR
452
453// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
454STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
455STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
456#ifndef STBI_NO_STDIO
457STBIDEF int stbi_is_hdr (char const *filename);
458STBIDEF int stbi_is_hdr_from_file(FILE *f);
459#endif // STBI_NO_STDIO
460
461
462// get a VERY brief reason for failure
463// NOT THREADSAFE
464STBIDEF const char *stbi_failure_reason (void);
465
466// free the loaded image -- this is just free()
467STBIDEF void stbi_image_free (void *retval_from_stbi_load);
468
469// get image dimensions & components without fully decoding
470STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
471STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
472
473#ifndef STBI_NO_STDIO
474STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
475STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
476
477#endif
478
479
480
481// for image formats that explicitly notate that they have premultiplied alpha,
482// we just return the colors as stored in the file. set this flag to force
483// unpremultiplication. results are undefined if the unpremultiply overflow.
484STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
485
486// indicate whether we should process iphone images back to canonical format,
487// or just pass them through "as-is"
488STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
489
490
491// ZLIB client - used by PNG, available for other purposes
492
493STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
494STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
495STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
496STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
497
498STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
499STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
500
501
502#ifdef __cplusplus
503}
504#endif
505
506//
507//
508//// end header file /////////////////////////////////////////////////////
509#endif // STBI_INCLUDE_STB_IMAGE_H
510
511#ifdef STB_IMAGE_IMPLEMENTATION
512
513#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
514 || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
515 || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
516 || defined(STBI_ONLY_ZLIB)
517 #ifndef STBI_ONLY_JPEG
518 #define STBI_NO_JPEG
519 #endif
520 #ifndef STBI_ONLY_PNG
521 #define STBI_NO_PNG
522 #endif
523 #ifndef STBI_ONLY_BMP
524 #define STBI_NO_BMP
525 #endif
526 #ifndef STBI_ONLY_PSD
527 #define STBI_NO_PSD
528 #endif
529 #ifndef STBI_ONLY_TGA
530 #define STBI_NO_TGA
531 #endif
532 #ifndef STBI_ONLY_GIF
533 #define STBI_NO_GIF
534 #endif
535 #ifndef STBI_ONLY_HDR
536 #define STBI_NO_HDR
537 #endif
538 #ifndef STBI_ONLY_PIC
539 #define STBI_NO_PIC
540 #endif
541 #ifndef STBI_ONLY_PNM
542 #define STBI_NO_PNM
543 #endif
544#endif
545
546#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
547#define STBI_NO_ZLIB
548#endif
549
550
551#include <stdarg.h>
552#include <stddef.h> // ptrdiff_t on osx
553#include <stdlib.h>
554#include <string.h>
555
556#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
557#include <math.h> // ldexp
558#endif
559
560#ifndef STBI_NO_STDIO
561#include <stdio.h>
562#endif
563
564#ifndef STBI_ASSERT
565#include <assert.h>
566#define STBI_ASSERT(x) assert(x)
567#endif
568
569
570#ifndef _MSC_VER
571 #ifdef __cplusplus
572 #define stbi_inline inline
573 #else
574 #define stbi_inline
575 #endif
576#else
577 #define stbi_inline __forceinline
578#endif
579
580
581#ifdef _MSC_VER
582typedef unsigned short stbi__uint16;
583typedef signed short stbi__int16;
584typedef unsigned int stbi__uint32;
585typedef signed int stbi__int32;
586#else
587#include <stdint.h>
588typedef uint16_t stbi__uint16;
589typedef int16_t stbi__int16;
590typedef uint32_t stbi__uint32;
591typedef int32_t stbi__int32;
592#endif
593
594// should produce compiler error if size is wrong
595typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
596
597#ifdef _MSC_VER
598#define STBI_NOTUSED(v) (void)(v)
599#else
600#define STBI_NOTUSED(v) (void)sizeof(v)
601#endif
602
603#ifdef _MSC_VER
604#define STBI_HAS_LROTL
605#endif
606
607#ifdef STBI_HAS_LROTL
608 #define stbi_lrot(x,y) _lrotl(x,y)
609#else
610 #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
611#endif
612
613#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC)
614// ok
615#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC)
616// ok
617#else
618#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC."
619#endif
620
621#ifndef STBI_MALLOC
622#define STBI_MALLOC(sz) malloc(sz)
623#define STBI_REALLOC(p,sz) realloc(p,sz)
624#define STBI_FREE(p) free(p)
625#endif
626
627#if defined(__GNUC__) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
628// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
629// (but compiling with -msse2 allows the compiler to use SSE2 everywhere;
630// this is just broken and gcc are jerks for not fixing it properly
631// http://www.virtualdub.org/blog/pivot/entry.php?id=363 )
632#define STBI_NO_SIMD
633#endif
634
635#if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
636#define STBI_SSE2
637#include <emmintrin.h>
638
639#ifdef _MSC_VER
640
641#if _MSC_VER >= 1400 // not VC6
642#include <intrin.h> // __cpuid
643static int stbi__cpuid3(void)
644{
645 int info[4];
646 __cpuid(info,1);
647 return info[3];
648}
649#else
650static int stbi__cpuid3(void)
651{
652 int res;
653 __asm {
654 mov eax,1
655 cpuid
656 mov res,edx
657 }
658 return res;
659}
660#endif
661
662#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
663
664static int stbi__sse2_available()
665{
666 int info3 = stbi__cpuid3();
667 return ((info3 >> 26) & 1) != 0;
668}
669#else // assume GCC-style if not VC++
670#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
671
672static int stbi__sse2_available()
673{
674#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later
675 // GCC 4.8+ has a nice way to do this
676 return __builtin_cpu_supports("sse2");
677#else
678 // portable way to do this, preferably without using GCC inline ASM?
679 // just bail for now.
680 return 0;
681#endif
682}
683#endif
684#endif
685
686// ARM NEON
687#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
688#undef STBI_NEON
689#endif
690
691#ifdef STBI_NEON
692#include <arm_neon.h>
693// assume GCC or Clang on ARM targets
694#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
695#endif
696
697#ifndef STBI_SIMD_ALIGN
698#define STBI_SIMD_ALIGN(type, name) type name
699#endif
700
701///////////////////////////////////////////////
702//
703// stbi__context struct and start_xxx functions
704
705// stbi__context structure is our basic context used by all images, so it
706// contains all the IO context, plus some basic image information
707typedef struct
708{
709 stbi__uint32 img_x, img_y;
710 int img_n, img_out_n;
711
712 stbi_io_callbacks io;
713 void *io_user_data;
714
715 int read_from_callbacks;
716 int buflen;
717 stbi_uc buffer_start[128];
718
719 stbi_uc *img_buffer, *img_buffer_end;
720 stbi_uc *img_buffer_original;
721} stbi__context;
722
723
724static void stbi__refill_buffer(stbi__context *s);
725
726// initialize a memory-decode context
727static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
728{
729 s->io.read = NULL;
730 s->read_from_callbacks = 0;
731 s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
732 s->img_buffer_end = (stbi_uc *) buffer+len;
733}
734
735// initialize a callback-based context
736static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
737{
738 s->io = *c;
739 s->io_user_data = user;
740 s->buflen = sizeof(s->buffer_start);
741 s->read_from_callbacks = 1;
742 s->img_buffer_original = s->buffer_start;
743 stbi__refill_buffer(s);
744}
745
746#ifndef STBI_NO_STDIO
747
748static int stbi__stdio_read(void *user, char *data, int size)
749{
750 return (int) fread(data,1,size,(FILE*) user);
751}
752
753static void stbi__stdio_skip(void *user, int n)
754{
755 fseek((FILE*) user, n, SEEK_CUR);
756}
757
758static int stbi__stdio_eof(void *user)
759{
760 return feof((FILE*) user);
761}
762
763static stbi_io_callbacks stbi__stdio_callbacks =
764{
765 stbi__stdio_read,
766 stbi__stdio_skip,
767 stbi__stdio_eof,
768};
769
770static void stbi__start_file(stbi__context *s, FILE *f)
771{
772 stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
773}
774
775//static void stop_file(stbi__context *s) { }
776
777#endif // !STBI_NO_STDIO
778
779static void stbi__rewind(stbi__context *s)
780{
781 // conceptually rewind SHOULD rewind to the beginning of the stream,
782 // but we just rewind to the beginning of the initial buffer, because
783 // we only use it after doing 'test', which only ever looks at at most 92 bytes
784 s->img_buffer = s->img_buffer_original;
785}
786
787#ifndef STBI_NO_JPEG
788static int stbi__jpeg_test(stbi__context *s);
789static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
790static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);
791#endif
792
793#ifndef STBI_NO_PNG
794static int stbi__png_test(stbi__context *s);
795static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
796static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
797#endif
798
799#ifndef STBI_NO_BMP
800static int stbi__bmp_test(stbi__context *s);
801static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
802static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);
803#endif
804
805#ifndef STBI_NO_TGA
806static int stbi__tga_test(stbi__context *s);
807static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
808static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);
809#endif
810
811#ifndef STBI_NO_PSD
812static int stbi__psd_test(stbi__context *s);
813static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
814static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);
815#endif
816
817#ifndef STBI_NO_HDR
818static int stbi__hdr_test(stbi__context *s);
819static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
820static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);
821#endif
822
823#ifndef STBI_NO_PIC
824static int stbi__pic_test(stbi__context *s);
825static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
826static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
827#endif
828
829#ifndef STBI_NO_GIF
830static int stbi__gif_test(stbi__context *s);
831static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
832static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
833#endif
834
835#ifndef STBI_NO_PNM
836static int stbi__pnm_test(stbi__context *s);
837static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);
838static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
839#endif
840
841// this is not threadsafe
842static const char *stbi__g_failure_reason;
843
844STBIDEF const char *stbi_failure_reason(void)
845{
846 return stbi__g_failure_reason;
847}
848
849static int stbi__err(const char *str)
850{
851 stbi__g_failure_reason = str;
852 return 0;
853}
854
855static void *stbi__malloc(size_t size)
856{
857 return STBI_MALLOC(size);
858}
859
860// stbi__err - error
861// stbi__errpf - error returning pointer to float
862// stbi__errpuc - error returning pointer to unsigned char
863
864#ifdef STBI_NO_FAILURE_STRINGS
865 #define stbi__err(x,y) 0
866#elif defined(STBI_FAILURE_USERMSG)
867 #define stbi__err(x,y) stbi__err(y)
868#else
869 #define stbi__err(x,y) stbi__err(x)
870#endif
871
872#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL))
873#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL))
874
875STBIDEF void stbi_image_free(void *retval_from_stbi_load)
876{
877 STBI_FREE(retval_from_stbi_load);
878}
879
880#ifndef STBI_NO_LINEAR
881static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
882#endif
883
884#ifndef STBI_NO_HDR
885static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
886#endif
887
888static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
889{
890 #ifndef STBI_NO_JPEG
891 if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp);
892 #endif
893 #ifndef STBI_NO_PNG
894 if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp);
895 #endif
896 #ifndef STBI_NO_BMP
897 if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp);
898 #endif
899 #ifndef STBI_NO_GIF
900 if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp);
901 #endif
902 #ifndef STBI_NO_PSD
903 if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp);
904 #endif
905 #ifndef STBI_NO_PIC
906 if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp);
907 #endif
908 #ifndef STBI_NO_PNM
909 if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp);
910 #endif
911
912 #ifndef STBI_NO_HDR
913 if (stbi__hdr_test(s)) {
914 float *hdr = stbi__hdr_load(s, x,y,comp,req_comp);
915 return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
916 }
917 #endif
918
919 #ifndef STBI_NO_TGA
920 // test tga last because it's a crappy test!
921 if (stbi__tga_test(s))
922 return stbi__tga_load(s,x,y,comp,req_comp);
923 #endif
924
925 return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
926}
927
928#ifndef STBI_NO_STDIO
929
930static FILE *stbi__fopen(char const *filename, char const *mode)
931{
932 FILE *f;
933#if defined(_MSC_VER) && _MSC_VER >= 1400
934 if (0 != fopen_s(&f, filename, mode))
935 f=0;
936#else
937 f = fopen(filename, mode);
938#endif
939 return f;
940}
941
942
943STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
944{
945 FILE *f = stbi__fopen(filename, "rb");
946 unsigned char *result;
947 if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
948 result = stbi_load_from_file(f,x,y,comp,req_comp);
949 fclose(f);
950 return result;
951}
952
953STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
954{
955 unsigned char *result;
956 stbi__context s;
957 stbi__start_file(&s,f);
958 result = stbi_load_main(&s,x,y,comp,req_comp);
959 if (result) {
960 // need to 'unget' all the characters in the IO buffer
961 fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
962 }
963 return result;
964}
965#endif //!STBI_NO_STDIO
966
967STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
968{
969 stbi__context s;
970 stbi__start_mem(&s,buffer,len);
971 return stbi_load_main(&s,x,y,comp,req_comp);
972}
973
974STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
975{
976 stbi__context s;
977 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
978 return stbi_load_main(&s,x,y,comp,req_comp);
979}
980
981#ifndef STBI_NO_LINEAR
982static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
983{
984 unsigned char *data;
985 #ifndef STBI_NO_HDR
986 if (stbi__hdr_test(s))
987 return stbi__hdr_load(s,x,y,comp,req_comp);
988 #endif
989 data = stbi_load_main(s, x, y, comp, req_comp);
990 if (data)
991 return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
992 return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
993}
994
995STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
996{
997 stbi__context s;
998 stbi__start_mem(&s,buffer,len);
999 return stbi_loadf_main(&s,x,y,comp,req_comp);
1000}
1001
1002STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
1003{
1004 stbi__context s;
1005 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
1006 return stbi_loadf_main(&s,x,y,comp,req_comp);
1007}
1008
1009#ifndef STBI_NO_STDIO
1010STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
1011{
1012 float *result;
1013 FILE *f = stbi__fopen(filename, "rb");
1014 if (!f) return stbi__errpf("can't fopen", "Unable to open file");
1015 result = stbi_loadf_from_file(f,x,y,comp,req_comp);
1016 fclose(f);
1017 return result;
1018}
1019
1020STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
1021{
1022 stbi__context s;
1023 stbi__start_file(&s,f);
1024 return stbi_loadf_main(&s,x,y,comp,req_comp);
1025}
1026#endif // !STBI_NO_STDIO
1027
1028#endif // !STBI_NO_LINEAR
1029
1030// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
1031// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
1032// reports false!
1033
1034STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
1035{
1036 #ifndef STBI_NO_HDR
1037 stbi__context s;
1038 stbi__start_mem(&s,buffer,len);
1039 return stbi__hdr_test(&s);
1040 #else
1041 STBI_NOTUSED(buffer);
1042 STBI_NOTUSED(len);
1043 return 0;
1044 #endif
1045}
1046
1047#ifndef STBI_NO_STDIO
1048STBIDEF int stbi_is_hdr (char const *filename)
1049{
1050 FILE *f = stbi__fopen(filename, "rb");
1051 int result=0;
1052 if (f) {
1053 result = stbi_is_hdr_from_file(f);
1054 fclose(f);
1055 }
1056 return result;
1057}
1058
1059STBIDEF int stbi_is_hdr_from_file(FILE *f)
1060{
1061 #ifndef STBI_NO_HDR
1062 stbi__context s;
1063 stbi__start_file(&s,f);
1064 return stbi__hdr_test(&s);
1065 #else
1066 return 0;
1067 #endif
1068}
1069#endif // !STBI_NO_STDIO
1070
1071STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
1072{
1073 #ifndef STBI_NO_HDR
1074 stbi__context s;
1075 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
1076 return stbi__hdr_test(&s);
1077 #else
1078 return 0;
1079 #endif
1080}
1081
1082static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
1083static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
1084
1085#ifndef STBI_NO_LINEAR
1086STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
1087STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
1088#endif
1089
1090STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
1091STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
1092
1093
1094//////////////////////////////////////////////////////////////////////////////
1095//
1096// Common code used by all image loaders
1097//
1098
1099enum
1100{
1101 STBI__SCAN_load=0,
1102 STBI__SCAN_type,
1103 STBI__SCAN_header
1104};
1105
1106static void stbi__refill_buffer(stbi__context *s)
1107{
1108 int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
1109 if (n == 0) {
1110 // at end of file, treat same as if from memory, but need to handle case
1111 // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
1112 s->read_from_callbacks = 0;
1113 s->img_buffer = s->buffer_start;
1114 s->img_buffer_end = s->buffer_start+1;
1115 *s->img_buffer = 0;
1116 } else {
1117 s->img_buffer = s->buffer_start;
1118 s->img_buffer_end = s->buffer_start + n;
1119 }
1120}
1121
1122stbi_inline static stbi_uc stbi__get8(stbi__context *s)
1123{
1124 if (s->img_buffer < s->img_buffer_end)
1125 return *s->img_buffer++;
1126 if (s->read_from_callbacks) {
1127 stbi__refill_buffer(s);
1128 return *s->img_buffer++;
1129 }
1130 return 0;
1131}
1132
1133stbi_inline static int stbi__at_eof(stbi__context *s)
1134{
1135 if (s->io.read) {
1136 if (!(s->io.eof)(s->io_user_data)) return 0;
1137 // if feof() is true, check if buffer = end
1138 // special case: we've only got the special 0 character at the end
1139 if (s->read_from_callbacks == 0) return 1;
1140 }
1141
1142 return s->img_buffer >= s->img_buffer_end;
1143}
1144
1145static void stbi__skip(stbi__context *s, int n)
1146{
1147 if (s->io.read) {
1148 int blen = (int) (s->img_buffer_end - s->img_buffer);
1149 if (blen < n) {
1150 s->img_buffer = s->img_buffer_end;
1151 (s->io.skip)(s->io_user_data, n - blen);
1152 return;
1153 }
1154 }
1155 s->img_buffer += n;
1156}
1157
1158static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
1159{
1160 if (s->io.read) {
1161 int blen = (int) (s->img_buffer_end - s->img_buffer);
1162 if (blen < n) {
1163 int res, count;
1164
1165 memcpy(buffer, s->img_buffer, blen);
1166
1167 count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
1168 res = (count == (n-blen));
1169 s->img_buffer = s->img_buffer_end;
1170 return res;
1171 }
1172 }
1173
1174 if (s->img_buffer+n <= s->img_buffer_end) {
1175 memcpy(buffer, s->img_buffer, n);
1176 s->img_buffer += n;
1177 return 1;
1178 } else
1179 return 0;
1180}
1181
1182static int stbi__get16be(stbi__context *s)
1183{
1184 int z = stbi__get8(s);
1185 return (z << 8) + stbi__get8(s);
1186}
1187
1188static stbi__uint32 stbi__get32be(stbi__context *s)
1189{
1190 stbi__uint32 z = stbi__get16be(s);
1191 return (z << 16) + stbi__get16be(s);
1192}
1193
1194static int stbi__get16le(stbi__context *s)
1195{
1196 int z = stbi__get8(s);
1197 return z + (stbi__get8(s) << 8);
1198}
1199
1200static stbi__uint32 stbi__get32le(stbi__context *s)
1201{
1202 stbi__uint32 z = stbi__get16le(s);
1203 return z + (stbi__get16le(s) << 16);
1204}
1205
1206#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
1207
1208
1209//////////////////////////////////////////////////////////////////////////////
1210//
1211// generic converter from built-in img_n to req_comp
1212// individual types do this automatically as much as possible (e.g. jpeg
1213// does all cases internally since it needs to colorspace convert anyway,
1214// and it never has alpha, so very few cases ). png can automatically
1215// interleave an alpha=255 channel, but falls back to this for other cases
1216//
1217// assume data buffer is malloced, so malloc a new one and free that one
1218// only failure mode is malloc failing
1219
1220static stbi_uc stbi__compute_y(int r, int g, int b)
1221{
1222 return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
1223}
1224
1225static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
1226{
1227 int i,j;
1228 unsigned char *good;
1229
1230 if (req_comp == img_n) return data;
1231 STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
1232
1233 good = (unsigned char *) stbi__malloc(req_comp * x * y);
1234 if (good == NULL) {
1235 STBI_FREE(data);
1236 return stbi__errpuc("outofmem", "Out of memory");
1237 }
1238
1239 for (j=0; j < (int) y; ++j) {
1240 unsigned char *src = data + j * x * img_n ;
1241 unsigned char *dest = good + j * x * req_comp;
1242
1243 #define COMBO(a,b) ((a)*8+(b))
1244 #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
1245 // convert source image with img_n components to one with req_comp components;
1246 // avoid switch per pixel, so use switch per scanline and massive macros
1247 switch (COMBO(img_n, req_comp)) {
1248 CASE(1,2) dest[0]=src[0], dest[1]=255; break;
1249 CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;
1250 CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;
1251 CASE(2,1) dest[0]=src[0]; break;
1252 CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;
1253 CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;
1254 CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;
1255 CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break;
1256 CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break;
1257 CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break;
1258 CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;
1259 CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;
1260 default: STBI_ASSERT(0);
1261 }
1262 #undef CASE
1263 }
1264
1265 STBI_FREE(data);
1266 return good;
1267}
1268
1269#ifndef STBI_NO_LINEAR
1270static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
1271{
1272 int i,k,n;
1273 float *output = (float *) stbi__malloc(x * y * comp * sizeof(float));
1274 if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); }
1275 // compute number of non-alpha components
1276 if (comp & 1) n = comp; else n = comp-1;
1277 for (i=0; i < x*y; ++i) {
1278 for (k=0; k < n; ++k) {
1279 output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
1280 }
1281 if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
1282 }
1283 STBI_FREE(data);
1284 return output;
1285}
1286#endif
1287
1288#ifndef STBI_NO_HDR
1289#define stbi__float2int(x) ((int) (x))
1290static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
1291{
1292 int i,k,n;
1293 stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp);
1294 if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); }
1295 // compute number of non-alpha components
1296 if (comp & 1) n = comp; else n = comp-1;
1297 for (i=0; i < x*y; ++i) {
1298 for (k=0; k < n; ++k) {
1299 float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
1300 if (z < 0) z = 0;
1301 if (z > 255) z = 255;
1302 output[i*comp + k] = (stbi_uc) stbi__float2int(z);
1303 }
1304 if (k < comp) {
1305 float z = data[i*comp+k] * 255 + 0.5f;
1306 if (z < 0) z = 0;
1307 if (z > 255) z = 255;
1308 output[i*comp + k] = (stbi_uc) stbi__float2int(z);
1309 }
1310 }
1311 STBI_FREE(data);
1312 return output;
1313}
1314#endif
1315
1316//////////////////////////////////////////////////////////////////////////////
1317//
1318// "baseline" JPEG/JFIF decoder
1319//
1320// simple implementation
1321// - doesn't support delayed output of y-dimension
1322// - simple interface (only one output format: 8-bit interleaved RGB)
1323// - doesn't try to recover corrupt jpegs
1324// - doesn't allow partial loading, loading multiple at once
1325// - still fast on x86 (copying globals into locals doesn't help x86)
1326// - allocates lots of intermediate memory (full size of all components)
1327// - non-interleaved case requires this anyway
1328// - allows good upsampling (see next)
1329// high-quality
1330// - upsampled channels are bilinearly interpolated, even across blocks
1331// - quality integer IDCT derived from IJG's 'slow'
1332// performance
1333// - fast huffman; reasonable integer IDCT
1334// - some SIMD kernels for common paths on targets with SSE2/NEON
1335// - uses a lot of intermediate memory, could cache poorly
1336
1337#ifndef STBI_NO_JPEG
1338
1339// huffman decoding acceleration
1340#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
1341
1342typedef struct
1343{
1344 stbi_uc fast[1 << FAST_BITS];
1345 // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
1346 stbi__uint16 code[256];
1347 stbi_uc values[256];
1348 stbi_uc size[257];
1349 unsigned int maxcode[18];
1350 int delta[17]; // old 'firstsymbol' - old 'firstcode'
1351} stbi__huffman;
1352
1353typedef struct
1354{
1355 stbi__context *s;
1356 stbi__huffman huff_dc[4];
1357 stbi__huffman huff_ac[4];
1358 stbi_uc dequant[4][64];
1359 stbi__int16 fast_ac[4][1 << FAST_BITS];
1360
1361// sizes for components, interleaved MCUs
1362 int img_h_max, img_v_max;
1363 int img_mcu_x, img_mcu_y;
1364 int img_mcu_w, img_mcu_h;
1365
1366// definition of jpeg image component
1367 struct
1368 {
1369 int id;
1370 int h,v;
1371 int tq;
1372 int hd,ha;
1373 int dc_pred;
1374
1375 int x,y,w2,h2;
1376 stbi_uc *data;
1377 void *raw_data, *raw_coeff;
1378 stbi_uc *linebuf;
1379 short *coeff; // progressive only
1380 int coeff_w, coeff_h; // number of 8x8 coefficient blocks
1381 } img_comp[4];
1382
1383 stbi__uint32 code_buffer; // jpeg entropy-coded buffer
1384 int code_bits; // number of valid bits
1385 unsigned char marker; // marker seen while filling entropy buffer
1386 int nomore; // flag if we saw a marker so must stop
1387
1388 int progressive;
1389 int spec_start;
1390 int spec_end;
1391 int succ_high;
1392 int succ_low;
1393 int eob_run;
1394
1395 int scan_n, order[4];
1396 int restart_interval, todo;
1397
1398// kernels
1399 void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);
1400 void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);
1401 stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);
1402} stbi__jpeg;
1403
1404static int stbi__build_huffman(stbi__huffman *h, int *count)
1405{
1406 int i,j,k=0,code;
1407 // build size list for each symbol (from JPEG spec)
1408 for (i=0; i < 16; ++i)
1409 for (j=0; j < count[i]; ++j)
1410 h->size[k++] = (stbi_uc) (i+1);
1411 h->size[k] = 0;
1412
1413 // compute actual symbols (from jpeg spec)
1414 code = 0;
1415 k = 0;
1416 for(j=1; j <= 16; ++j) {
1417 // compute delta to add to code to compute symbol id
1418 h->delta[j] = k - code;
1419 if (h->size[k] == j) {
1420 while (h->size[k] == j)
1421 h->code[k++] = (stbi__uint16) (code++);
1422 if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG");
1423 }
1424 // compute largest code + 1 for this size, preshifted as needed later
1425 h->maxcode[j] = code << (16-j);
1426 code <<= 1;
1427 }
1428 h->maxcode[j] = 0xffffffff;
1429
1430 // build non-spec acceleration table; 255 is flag for not-accelerated
1431 memset(h->fast, 255, 1 << FAST_BITS);
1432 for (i=0; i < k; ++i) {
1433 int s = h->size[i];
1434 if (s <= FAST_BITS) {
1435 int c = h->code[i] << (FAST_BITS-s);
1436 int m = 1 << (FAST_BITS-s);
1437 for (j=0; j < m; ++j) {
1438 h->fast[c+j] = (stbi_uc) i;
1439 }
1440 }
1441 }
1442 return 1;
1443}
1444
1445// build a table that decodes both magnitude and value of small ACs in
1446// one go.
1447static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)
1448{
1449 int i;
1450 for (i=0; i < (1 << FAST_BITS); ++i) {
1451 stbi_uc fast = h->fast[i];
1452 fast_ac[i] = 0;
1453 if (fast < 255) {
1454 int rs = h->values[fast];
1455 int run = (rs >> 4) & 15;
1456 int magbits = rs & 15;
1457 int len = h->size[fast];
1458
1459 if (magbits && len + magbits <= FAST_BITS) {
1460 // magnitude code followed by receive_extend code
1461 int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
1462 int m = 1 << (magbits - 1);
1463 if (k < m) k += (-1 << magbits) + 1;
1464 // if the result is small enough, we can fit it in fast_ac table
1465 if (k >= -128 && k <= 127)
1466 fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits));
1467 }
1468 }
1469 }
1470}
1471
1472static void stbi__grow_buffer_unsafe(stbi__jpeg *j)
1473{
1474 do {
1475 int b = j->nomore ? 0 : stbi__get8(j->s);
1476 if (b == 0xff) {
1477 int c = stbi__get8(j->s);
1478 if (c != 0) {
1479 j->marker = (unsigned char) c;
1480 j->nomore = 1;
1481 return;
1482 }
1483 }
1484 j->code_buffer |= b << (24 - j->code_bits);
1485 j->code_bits += 8;
1486 } while (j->code_bits <= 24);
1487}
1488
1489// (1 << n) - 1
1490static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
1491
1492// decode a jpeg huffman value from the bitstream
1493stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
1494{
1495 unsigned int temp;
1496 int c,k;
1497
1498 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1499
1500 // look at the top FAST_BITS and determine what symbol ID it is,
1501 // if the code is <= FAST_BITS
1502 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
1503 k = h->fast[c];
1504 if (k < 255) {
1505 int s = h->size[k];
1506 if (s > j->code_bits)
1507 return -1;
1508 j->code_buffer <<= s;
1509 j->code_bits -= s;
1510 return h->values[k];
1511 }
1512
1513 // naive test is to shift the code_buffer down so k bits are
1514 // valid, then test against maxcode. To speed this up, we've
1515 // preshifted maxcode left so that it has (16-k) 0s at the
1516 // end; in other words, regardless of the number of bits, it
1517 // wants to be compared against something shifted to have 16;
1518 // that way we don't need to shift inside the loop.
1519 temp = j->code_buffer >> 16;
1520 for (k=FAST_BITS+1 ; ; ++k)
1521 if (temp < h->maxcode[k])
1522 break;
1523 if (k == 17) {
1524 // error! code not found
1525 j->code_bits -= 16;
1526 return -1;
1527 }
1528
1529 if (k > j->code_bits)
1530 return -1;
1531
1532 // convert the huffman code to the symbol id
1533 c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
1534 STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
1535
1536 // convert the id to a symbol
1537 j->code_bits -= k;
1538 j->code_buffer <<= k;
1539 return h->values[c];
1540}
1541
1542// bias[n] = (-1<<n) + 1
1543static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};
1544
1545// combined JPEG 'receive' and JPEG 'extend', since baseline
1546// always extends everything it receives.
1547stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
1548{
1549 unsigned int k;
1550 int sgn;
1551 if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
1552
1553 sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
1554 k = stbi_lrot(j->code_buffer, n);
1555 j->code_buffer = k & ~stbi__bmask[n];
1556 k &= stbi__bmask[n];
1557 j->code_bits -= n;
1558 return k + (stbi__jbias[n] & ~sgn);
1559}
1560
1561// get some unsigned bits
1562stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
1563{
1564 unsigned int k;
1565 if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
1566 k = stbi_lrot(j->code_buffer, n);
1567 j->code_buffer = k & ~stbi__bmask[n];
1568 k &= stbi__bmask[n];
1569 j->code_bits -= n;
1570 return k;
1571}
1572
1573stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
1574{
1575 unsigned int k;
1576 if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
1577 k = j->code_buffer;
1578 j->code_buffer <<= 1;
1579 --j->code_bits;
1580 return k & 0x80000000;
1581}
1582
1583// given a value that's at position X in the zigzag stream,
1584// where does it appear in the 8x8 matrix coded as row-major?
1585static stbi_uc stbi__jpeg_dezigzag[64+15] =
1586{
1587 0, 1, 8, 16, 9, 2, 3, 10,
1588 17, 24, 32, 25, 18, 11, 4, 5,
1589 12, 19, 26, 33, 40, 48, 41, 34,
1590 27, 20, 13, 6, 7, 14, 21, 28,
1591 35, 42, 49, 56, 57, 50, 43, 36,
1592 29, 22, 15, 23, 30, 37, 44, 51,
1593 58, 59, 52, 45, 38, 31, 39, 46,
1594 53, 60, 61, 54, 47, 55, 62, 63,
1595 // let corrupt input sample past end
1596 63, 63, 63, 63, 63, 63, 63, 63,
1597 63, 63, 63, 63, 63, 63, 63
1598};
1599
1600// decode one 64-entry block--
1601static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant)
1602{
1603 int diff,dc,k;
1604 int t;
1605
1606 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1607 t = stbi__jpeg_huff_decode(j, hdc);
1608 if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
1609
1610 // 0 all the ac values now so we can do it 32-bits at a time
1611 memset(data,0,64*sizeof(data[0]));
1612
1613 diff = t ? stbi__extend_receive(j, t) : 0;
1614 dc = j->img_comp[b].dc_pred + diff;
1615 j->img_comp[b].dc_pred = dc;
1616 data[0] = (short) (dc * dequant[0]);
1617
1618 // decode AC components, see JPEG spec
1619 k = 1;
1620 do {
1621 unsigned int zig;
1622 int c,r,s;
1623 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1624 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
1625 r = fac[c];
1626 if (r) { // fast-AC path
1627 k += (r >> 4) & 15; // run
1628 s = r & 15; // combined length
1629 j->code_buffer <<= s;
1630 j->code_bits -= s;
1631 // decode into unzigzag'd location
1632 zig = stbi__jpeg_dezigzag[k++];
1633 data[zig] = (short) ((r >> 8) * dequant[zig]);
1634 } else {
1635 int rs = stbi__jpeg_huff_decode(j, hac);
1636 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
1637 s = rs & 15;
1638 r = rs >> 4;
1639 if (s == 0) {
1640 if (rs != 0xf0) break; // end block
1641 k += 16;
1642 } else {
1643 k += r;
1644 // decode into unzigzag'd location
1645 zig = stbi__jpeg_dezigzag[k++];
1646 data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);
1647 }
1648 }
1649 } while (k < 64);
1650 return 1;
1651}
1652
1653static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)
1654{
1655 int diff,dc;
1656 int t;
1657 if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
1658
1659 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1660
1661 if (j->succ_high == 0) {
1662 // first scan for DC coefficient, must be first
1663 memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
1664 t = stbi__jpeg_huff_decode(j, hdc);
1665 diff = t ? stbi__extend_receive(j, t) : 0;
1666
1667 dc = j->img_comp[b].dc_pred + diff;
1668 j->img_comp[b].dc_pred = dc;
1669 data[0] = (short) (dc << j->succ_low);
1670 } else {
1671 // refinement scan for DC coefficient
1672 if (stbi__jpeg_get_bit(j))
1673 data[0] += (short) (1 << j->succ_low);
1674 }
1675 return 1;
1676}
1677
1678// @OPTIMIZE: store non-zigzagged during the decode passes,
1679// and only de-zigzag when dequantizing
1680static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)
1681{
1682 int k;
1683 if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
1684
1685 if (j->succ_high == 0) {
1686 int shift = j->succ_low;
1687
1688 if (j->eob_run) {
1689 --j->eob_run;
1690 return 1;
1691 }
1692
1693 k = j->spec_start;
1694 do {
1695 unsigned int zig;
1696 int c,r,s;
1697 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1698 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
1699 r = fac[c];
1700 if (r) { // fast-AC path
1701 k += (r >> 4) & 15; // run
1702 s = r & 15; // combined length
1703 j->code_buffer <<= s;
1704 j->code_bits -= s;
1705 zig = stbi__jpeg_dezigzag[k++];
1706 data[zig] = (short) ((r >> 8) << shift);
1707 } else {
1708 int rs = stbi__jpeg_huff_decode(j, hac);
1709 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
1710 s = rs & 15;
1711 r = rs >> 4;
1712 if (s == 0) {
1713 if (r < 15) {
1714 j->eob_run = (1 << r);
1715 if (r)
1716 j->eob_run += stbi__jpeg_get_bits(j, r);
1717 --j->eob_run;
1718 break;
1719 }
1720 k += 16;
1721 } else {
1722 k += r;
1723 zig = stbi__jpeg_dezigzag[k++];
1724 data[zig] = (short) (stbi__extend_receive(j,s) << shift);
1725 }
1726 }
1727 } while (k <= j->spec_end);
1728 } else {
1729 // refinement scan for these AC coefficients
1730
1731 short bit = (short) (1 << j->succ_low);
1732
1733 if (j->eob_run) {
1734 --j->eob_run;
1735 for (k = j->spec_start; k <= j->spec_end; ++k) {
1736 short *p = &data[stbi__jpeg_dezigzag[k]];
1737 if (*p != 0)
1738 if (stbi__jpeg_get_bit(j))
1739 if ((*p & bit)==0) {
1740 if (*p > 0)
1741 *p += bit;
1742 else
1743 *p -= bit;
1744 }
1745 }
1746 } else {
1747 k = j->spec_start;
1748 do {
1749 int r,s;
1750 int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
1751 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
1752 s = rs & 15;
1753 r = rs >> 4;
1754 if (s == 0) {
1755 if (r < 15) {
1756 j->eob_run = (1 << r) - 1;
1757 if (r)
1758 j->eob_run += stbi__jpeg_get_bits(j, r);
1759 r = 64; // force end of block
1760 } else
1761 r = 16; // r=15 is the code for 16 0s
1762 } else {
1763 if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
1764 // sign bit
1765 if (stbi__jpeg_get_bit(j))
1766 s = bit;
1767 else
1768 s = -bit;
1769 }
1770
1771 // advance by r
1772 while (k <= j->spec_end) {
1773 short *p = &data[stbi__jpeg_dezigzag[k]];
1774 if (*p != 0) {
1775 if (stbi__jpeg_get_bit(j))
1776 if ((*p & bit)==0) {
1777 if (*p > 0)
1778 *p += bit;
1779 else
1780 *p -= bit;
1781 }
1782 ++k;
1783 } else {
1784 if (r == 0) {
1785 if (s)
1786 data[stbi__jpeg_dezigzag[k++]] = (short) s;
1787 break;
1788 }
1789 --r;
1790 ++k;
1791 }
1792 }
1793 } while (k <= j->spec_end);
1794 }
1795 }
1796 return 1;
1797}
1798
1799// take a -128..127 value and stbi__clamp it and convert to 0..255
1800stbi_inline static stbi_uc stbi__clamp(int x)
1801{
1802 // trick to use a single test to catch both cases
1803 if ((unsigned int) x > 255) {
1804 if (x < 0) return 0;
1805 if (x > 255) return 255;
1806 }
1807 return (stbi_uc) x;
1808}
1809
1810#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5)))
1811#define stbi__fsh(x) ((x) << 12)
1812
1813// derived from jidctint -- DCT_ISLOW
1814#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
1815 int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
1816 p2 = s2; \
1817 p3 = s6; \
1818 p1 = (p2+p3) * stbi__f2f(0.5411961f); \
1819 t2 = p1 + p3*stbi__f2f(-1.847759065f); \
1820 t3 = p1 + p2*stbi__f2f( 0.765366865f); \
1821 p2 = s0; \
1822 p3 = s4; \
1823 t0 = stbi__fsh(p2+p3); \
1824 t1 = stbi__fsh(p2-p3); \
1825 x0 = t0+t3; \
1826 x3 = t0-t3; \
1827 x1 = t1+t2; \
1828 x2 = t1-t2; \
1829 t0 = s7; \
1830 t1 = s5; \
1831 t2 = s3; \
1832 t3 = s1; \
1833 p3 = t0+t2; \
1834 p4 = t1+t3; \
1835 p1 = t0+t3; \
1836 p2 = t1+t2; \
1837 p5 = (p3+p4)*stbi__f2f( 1.175875602f); \
1838 t0 = t0*stbi__f2f( 0.298631336f); \
1839 t1 = t1*stbi__f2f( 2.053119869f); \
1840 t2 = t2*stbi__f2f( 3.072711026f); \
1841 t3 = t3*stbi__f2f( 1.501321110f); \
1842 p1 = p5 + p1*stbi__f2f(-0.899976223f); \
1843 p2 = p5 + p2*stbi__f2f(-2.562915447f); \
1844 p3 = p3*stbi__f2f(-1.961570560f); \
1845 p4 = p4*stbi__f2f(-0.390180644f); \
1846 t3 += p1+p4; \
1847 t2 += p2+p3; \
1848 t1 += p2+p4; \
1849 t0 += p1+p3;
1850
1851static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
1852{
1853 int i,val[64],*v=val;
1854 stbi_uc *o;
1855 short *d = data;
1856
1857 // columns
1858 for (i=0; i < 8; ++i,++d, ++v) {
1859 // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
1860 if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
1861 && d[40]==0 && d[48]==0 && d[56]==0) {
1862 // no shortcut 0 seconds
1863 // (1|2|3|4|5|6|7)==0 0 seconds
1864 // all separate -0.047 seconds
1865 // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
1866 int dcterm = d[0] << 2;
1867 v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
1868 } else {
1869 STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])
1870 // constants scaled things up by 1<<12; let's bring them back
1871 // down, but keep 2 extra bits of precision
1872 x0 += 512; x1 += 512; x2 += 512; x3 += 512;
1873 v[ 0] = (x0+t3) >> 10;
1874 v[56] = (x0-t3) >> 10;
1875 v[ 8] = (x1+t2) >> 10;
1876 v[48] = (x1-t2) >> 10;
1877 v[16] = (x2+t1) >> 10;
1878 v[40] = (x2-t1) >> 10;
1879 v[24] = (x3+t0) >> 10;
1880 v[32] = (x3-t0) >> 10;
1881 }
1882 }
1883
1884 for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
1885 // no fast case since the first 1D IDCT spread components out
1886 STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
1887 // constants scaled things up by 1<<12, plus we had 1<<2 from first
1888 // loop, plus horizontal and vertical each scale by sqrt(8) so together
1889 // we've got an extra 1<<3, so 1<<17 total we need to remove.
1890 // so we want to round that, which means adding 0.5 * 1<<17,
1891 // aka 65536. Also, we'll end up with -128 to 127 that we want
1892 // to encode as 0..255 by adding 128, so we'll add that before the shift
1893 x0 += 65536 + (128<<17);
1894 x1 += 65536 + (128<<17);
1895 x2 += 65536 + (128<<17);
1896 x3 += 65536 + (128<<17);
1897 // tried computing the shifts into temps, or'ing the temps to see
1898 // if any were out of range, but that was slower
1899 o[0] = stbi__clamp((x0+t3) >> 17);
1900 o[7] = stbi__clamp((x0-t3) >> 17);
1901 o[1] = stbi__clamp((x1+t2) >> 17);
1902 o[6] = stbi__clamp((x1-t2) >> 17);
1903 o[2] = stbi__clamp((x2+t1) >> 17);
1904 o[5] = stbi__clamp((x2-t1) >> 17);
1905 o[3] = stbi__clamp((x3+t0) >> 17);
1906 o[4] = stbi__clamp((x3-t0) >> 17);
1907 }
1908}
1909
1910#ifdef STBI_SSE2
1911// sse2 integer IDCT. not the fastest possible implementation but it
1912// produces bit-identical results to the generic C version so it's
1913// fully "transparent".
1914static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
1915{
1916 // This is constructed to match our regular (generic) integer IDCT exactly.
1917 __m128i row0, row1, row2, row3, row4, row5, row6, row7;
1918 __m128i tmp;
1919
1920 // dot product constant: even elems=x, odd elems=y
1921 #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))
1922
1923 // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
1924 // out(1) = c1[even]*x + c1[odd]*y
1925 #define dct_rot(out0,out1, x,y,c0,c1) \
1926 __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
1927 __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
1928 __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
1929 __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
1930 __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
1931 __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
1932
1933 // out = in << 12 (in 16-bit, out 32-bit)
1934 #define dct_widen(out, in) \
1935 __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
1936 __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
1937
1938 // wide add
1939 #define dct_wadd(out, a, b) \
1940 __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
1941 __m128i out##_h = _mm_add_epi32(a##_h, b##_h)
1942
1943 // wide sub
1944 #define dct_wsub(out, a, b) \
1945 __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
1946 __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
1947
1948 // butterfly a/b, add bias, then shift by "s" and pack
1949 #define dct_bfly32o(out0, out1, a,b,bias,s) \
1950 { \
1951 __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
1952 __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
1953 dct_wadd(sum, abiased, b); \
1954 dct_wsub(dif, abiased, b); \
1955 out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
1956 out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
1957 }
1958
1959 // 8-bit interleave step (for transposes)
1960 #define dct_interleave8(a, b) \
1961 tmp = a; \
1962 a = _mm_unpacklo_epi8(a, b); \
1963 b = _mm_unpackhi_epi8(tmp, b)
1964
1965 // 16-bit interleave step (for transposes)
1966 #define dct_interleave16(a, b) \
1967 tmp = a; \
1968 a = _mm_unpacklo_epi16(a, b); \
1969 b = _mm_unpackhi_epi16(tmp, b)
1970
1971 #define dct_pass(bias,shift) \
1972 { \
1973 /* even part */ \
1974 dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
1975 __m128i sum04 = _mm_add_epi16(row0, row4); \
1976 __m128i dif04 = _mm_sub_epi16(row0, row4); \
1977 dct_widen(t0e, sum04); \
1978 dct_widen(t1e, dif04); \
1979 dct_wadd(x0, t0e, t3e); \
1980 dct_wsub(x3, t0e, t3e); \
1981 dct_wadd(x1, t1e, t2e); \
1982 dct_wsub(x2, t1e, t2e); \
1983 /* odd part */ \
1984 dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
1985 dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
1986 __m128i sum17 = _mm_add_epi16(row1, row7); \
1987 __m128i sum35 = _mm_add_epi16(row3, row5); \
1988 dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
1989 dct_wadd(x4, y0o, y4o); \
1990 dct_wadd(x5, y1o, y5o); \
1991 dct_wadd(x6, y2o, y5o); \
1992 dct_wadd(x7, y3o, y4o); \
1993 dct_bfly32o(row0,row7, x0,x7,bias,shift); \
1994 dct_bfly32o(row1,row6, x1,x6,bias,shift); \
1995 dct_bfly32o(row2,row5, x2,x5,bias,shift); \
1996 dct_bfly32o(row3,row4, x3,x4,bias,shift); \
1997 }
1998
1999 __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
2000 __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));
2001 __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
2002 __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
2003 __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));
2004 __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));
2005 __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));
2006 __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));
2007
2008 // rounding biases in column/row passes, see stbi__idct_block for explanation.
2009 __m128i bias_0 = _mm_set1_epi32(512);
2010 __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));
2011
2012 // load
2013 row0 = _mm_load_si128((const __m128i *) (data + 0*8));
2014 row1 = _mm_load_si128((const __m128i *) (data + 1*8));
2015 row2 = _mm_load_si128((const __m128i *) (data + 2*8));
2016 row3 = _mm_load_si128((const __m128i *) (data + 3*8));
2017 row4 = _mm_load_si128((const __m128i *) (data + 4*8));
2018 row5 = _mm_load_si128((const __m128i *) (data + 5*8));
2019 row6 = _mm_load_si128((const __m128i *) (data + 6*8));
2020 row7 = _mm_load_si128((const __m128i *) (data + 7*8));
2021
2022 // column pass
2023 dct_pass(bias_0, 10);
2024
2025 {
2026 // 16bit 8x8 transpose pass 1
2027 dct_interleave16(row0, row4);
2028 dct_interleave16(row1, row5);
2029 dct_interleave16(row2, row6);
2030 dct_interleave16(row3, row7);
2031
2032 // transpose pass 2
2033 dct_interleave16(row0, row2);
2034 dct_interleave16(row1, row3);
2035 dct_interleave16(row4, row6);
2036 dct_interleave16(row5, row7);
2037
2038 // transpose pass 3
2039 dct_interleave16(row0, row1);
2040 dct_interleave16(row2, row3);
2041 dct_interleave16(row4, row5);
2042 dct_interleave16(row6, row7);
2043 }
2044
2045 // row pass
2046 dct_pass(bias_1, 17);
2047
2048 {
2049 // pack
2050 __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
2051 __m128i p1 = _mm_packus_epi16(row2, row3);
2052 __m128i p2 = _mm_packus_epi16(row4, row5);
2053 __m128i p3 = _mm_packus_epi16(row6, row7);
2054
2055 // 8bit 8x8 transpose pass 1
2056 dct_interleave8(p0, p2); // a0e0a1e1...
2057 dct_interleave8(p1, p3); // c0g0c1g1...
2058
2059 // transpose pass 2
2060 dct_interleave8(p0, p1); // a0c0e0g0...
2061 dct_interleave8(p2, p3); // b0d0f0h0...
2062
2063 // transpose pass 3
2064 dct_interleave8(p0, p2); // a0b0c0d0...
2065 dct_interleave8(p1, p3); // a4b4c4d4...
2066
2067 // store
2068 _mm_storel_epi64((__m128i *) out, p0); out += out_stride;
2069 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;
2070 _mm_storel_epi64((__m128i *) out, p2); out += out_stride;
2071 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;
2072 _mm_storel_epi64((__m128i *) out, p1); out += out_stride;
2073 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;
2074 _mm_storel_epi64((__m128i *) out, p3); out += out_stride;
2075 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));
2076 }
2077
2078#undef dct_const
2079#undef dct_rot
2080#undef dct_widen
2081#undef dct_wadd
2082#undef dct_wsub
2083#undef dct_bfly32o
2084#undef dct_interleave8
2085#undef dct_interleave16
2086#undef dct_pass
2087}
2088
2089#endif // STBI_SSE2
2090
2091#ifdef STBI_NEON
2092
2093// NEON integer IDCT. should produce bit-identical
2094// results to the generic C version.
2095static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
2096{
2097 int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
2098
2099 int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
2100 int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
2101 int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));
2102 int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));
2103 int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
2104 int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
2105 int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
2106 int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
2107 int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));
2108 int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));
2109 int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));
2110 int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));
2111
2112#define dct_long_mul(out, inq, coeff) \
2113 int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
2114 int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
2115
2116#define dct_long_mac(out, acc, inq, coeff) \
2117 int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
2118 int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
2119
2120#define dct_widen(out, inq) \
2121 int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
2122 int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
2123
2124// wide add
2125#define dct_wadd(out, a, b) \
2126 int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
2127 int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
2128
2129// wide sub
2130#define dct_wsub(out, a, b) \
2131 int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
2132 int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
2133
2134// butterfly a/b, then shift using "shiftop" by "s" and pack
2135#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
2136 { \
2137 dct_wadd(sum, a, b); \
2138 dct_wsub(dif, a, b); \
2139 out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
2140 out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
2141 }
2142
2143#define dct_pass(shiftop, shift) \
2144 { \
2145 /* even part */ \
2146 int16x8_t sum26 = vaddq_s16(row2, row6); \
2147 dct_long_mul(p1e, sum26, rot0_0); \
2148 dct_long_mac(t2e, p1e, row6, rot0_1); \
2149 dct_long_mac(t3e, p1e, row2, rot0_2); \
2150 int16x8_t sum04 = vaddq_s16(row0, row4); \
2151 int16x8_t dif04 = vsubq_s16(row0, row4); \
2152 dct_widen(t0e, sum04); \
2153 dct_widen(t1e, dif04); \
2154 dct_wadd(x0, t0e, t3e); \
2155 dct_wsub(x3, t0e, t3e); \
2156 dct_wadd(x1, t1e, t2e); \
2157 dct_wsub(x2, t1e, t2e); \
2158 /* odd part */ \
2159 int16x8_t sum15 = vaddq_s16(row1, row5); \
2160 int16x8_t sum17 = vaddq_s16(row1, row7); \
2161 int16x8_t sum35 = vaddq_s16(row3, row5); \
2162 int16x8_t sum37 = vaddq_s16(row3, row7); \
2163 int16x8_t sumodd = vaddq_s16(sum17, sum35); \
2164 dct_long_mul(p5o, sumodd, rot1_0); \
2165 dct_long_mac(p1o, p5o, sum17, rot1_1); \
2166 dct_long_mac(p2o, p5o, sum35, rot1_2); \
2167 dct_long_mul(p3o, sum37, rot2_0); \
2168 dct_long_mul(p4o, sum15, rot2_1); \
2169 dct_wadd(sump13o, p1o, p3o); \
2170 dct_wadd(sump24o, p2o, p4o); \
2171 dct_wadd(sump23o, p2o, p3o); \
2172 dct_wadd(sump14o, p1o, p4o); \
2173 dct_long_mac(x4, sump13o, row7, rot3_0); \
2174 dct_long_mac(x5, sump24o, row5, rot3_1); \
2175 dct_long_mac(x6, sump23o, row3, rot3_2); \
2176 dct_long_mac(x7, sump14o, row1, rot3_3); \
2177 dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
2178 dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
2179 dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
2180 dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
2181 }
2182
2183 // load
2184 row0 = vld1q_s16(data + 0*8);
2185 row1 = vld1q_s16(data + 1*8);
2186 row2 = vld1q_s16(data + 2*8);
2187 row3 = vld1q_s16(data + 3*8);
2188 row4 = vld1q_s16(data + 4*8);
2189 row5 = vld1q_s16(data + 5*8);
2190 row6 = vld1q_s16(data + 6*8);
2191 row7 = vld1q_s16(data + 7*8);
2192
2193 // add DC bias
2194 row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
2195
2196 // column pass
2197 dct_pass(vrshrn_n_s32, 10);
2198
2199 // 16bit 8x8 transpose
2200 {
2201// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
2202// whether compilers actually get this is another story, sadly.
2203#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
2204#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
2205#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }
2206
2207 // pass 1
2208 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
2209 dct_trn16(row2, row3);
2210 dct_trn16(row4, row5);
2211 dct_trn16(row6, row7);
2212
2213 // pass 2
2214 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
2215 dct_trn32(row1, row3);
2216 dct_trn32(row4, row6);
2217 dct_trn32(row5, row7);
2218
2219 // pass 3
2220 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
2221 dct_trn64(row1, row5);
2222 dct_trn64(row2, row6);
2223 dct_trn64(row3, row7);
2224
2225#undef dct_trn16
2226#undef dct_trn32
2227#undef dct_trn64
2228 }
2229
2230 // row pass
2231 // vrshrn_n_s32 only supports shifts up to 16, we need
2232 // 17. so do a non-rounding shift of 16 first then follow
2233 // up with a rounding shift by 1.
2234 dct_pass(vshrn_n_s32, 16);
2235
2236 {
2237 // pack and round
2238 uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
2239 uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
2240 uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
2241 uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
2242 uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
2243 uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
2244 uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
2245 uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
2246
2247 // again, these can translate into one instruction, but often don't.
2248#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
2249#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
2250#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }
2251
2252 // sadly can't use interleaved stores here since we only write
2253 // 8 bytes to each scan line!
2254
2255 // 8x8 8-bit transpose pass 1
2256 dct_trn8_8(p0, p1);
2257 dct_trn8_8(p2, p3);
2258 dct_trn8_8(p4, p5);
2259 dct_trn8_8(p6, p7);
2260
2261 // pass 2
2262 dct_trn8_16(p0, p2);
2263 dct_trn8_16(p1, p3);
2264 dct_trn8_16(p4, p6);
2265 dct_trn8_16(p5, p7);
2266
2267 // pass 3
2268 dct_trn8_32(p0, p4);
2269 dct_trn8_32(p1, p5);
2270 dct_trn8_32(p2, p6);
2271 dct_trn8_32(p3, p7);
2272
2273 // store
2274 vst1_u8(out, p0); out += out_stride;
2275 vst1_u8(out, p1); out += out_stride;
2276 vst1_u8(out, p2); out += out_stride;
2277 vst1_u8(out, p3); out += out_stride;
2278 vst1_u8(out, p4); out += out_stride;
2279 vst1_u8(out, p5); out += out_stride;
2280 vst1_u8(out, p6); out += out_stride;
2281 vst1_u8(out, p7);
2282
2283#undef dct_trn8_8
2284#undef dct_trn8_16
2285#undef dct_trn8_32
2286 }
2287
2288#undef dct_long_mul
2289#undef dct_long_mac
2290#undef dct_widen
2291#undef dct_wadd
2292#undef dct_wsub
2293#undef dct_bfly32o
2294#undef dct_pass
2295}
2296
2297#endif // STBI_NEON
2298
2299#define STBI__MARKER_none 0xff
2300// if there's a pending marker from the entropy stream, return that
2301// otherwise, fetch from the stream and get a marker. if there's no
2302// marker, return 0xff, which is never a valid marker value
2303static stbi_uc stbi__get_marker(stbi__jpeg *j)
2304{
2305 stbi_uc x;
2306 if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }
2307 x = stbi__get8(j->s);
2308 if (x != 0xff) return STBI__MARKER_none;
2309 while (x == 0xff)
2310 x = stbi__get8(j->s);
2311 return x;
2312}
2313
2314// in each scan, we'll have scan_n components, and the order
2315// of the components is specified by order[]
2316#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
2317
2318// after a restart interval, stbi__jpeg_reset the entropy decoder and
2319// the dc prediction
2320static void stbi__jpeg_reset(stbi__jpeg *j)
2321{
2322 j->code_bits = 0;
2323 j->code_buffer = 0;
2324 j->nomore = 0;
2325 j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0;
2326 j->marker = STBI__MARKER_none;
2327 j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
2328 j->eob_run = 0;
2329 // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
2330 // since we don't even allow 1<<30 pixels
2331}
2332
2333static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
2334{
2335 stbi__jpeg_reset(z);
2336 if (!z->progressive) {
2337 if (z->scan_n == 1) {
2338 int i,j;
2339 STBI_SIMD_ALIGN(short, data[64]);
2340 int n = z->order[0];
2341 // non-interleaved data, we just need to process one block at a time,
2342 // in trivial scanline order
2343 // number of blocks to do just depends on how many actual "pixels" this
2344 // component has, independent of interleaved MCU blocking and such
2345 int w = (z->img_comp[n].x+7) >> 3;
2346 int h = (z->img_comp[n].y+7) >> 3;
2347 for (j=0; j < h; ++j) {
2348 for (i=0; i < w; ++i) {
2349 int ha = z->img_comp[n].ha;
2350 if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
2351 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
2352 // every data block is an MCU, so countdown the restart interval
2353 if (--z->todo <= 0) {
2354 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2355 // if it's NOT a restart, then just bail, so we get corrupt data
2356 // rather than no data
2357 if (!STBI__RESTART(z->marker)) return 1;
2358 stbi__jpeg_reset(z);
2359 }
2360 }
2361 }
2362 return 1;
2363 } else { // interleaved
2364 int i,j,k,x,y;
2365 STBI_SIMD_ALIGN(short, data[64]);
2366 for (j=0; j < z->img_mcu_y; ++j) {
2367 for (i=0; i < z->img_mcu_x; ++i) {
2368 // scan an interleaved mcu... process scan_n components in order
2369 for (k=0; k < z->scan_n; ++k) {
2370 int n = z->order[k];
2371 // scan out an mcu's worth of this component; that's just determined
2372 // by the basic H and V specified for the component
2373 for (y=0; y < z->img_comp[n].v; ++y) {
2374 for (x=0; x < z->img_comp[n].h; ++x) {
2375 int x2 = (i*z->img_comp[n].h + x)*8;
2376 int y2 = (j*z->img_comp[n].v + y)*8;
2377 int ha = z->img_comp[n].ha;
2378 if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
2379 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);
2380 }
2381 }
2382 }
2383 // after all interleaved components, that's an interleaved MCU,
2384 // so now count down the restart interval
2385 if (--z->todo <= 0) {
2386 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2387 if (!STBI__RESTART(z->marker)) return 1;
2388 stbi__jpeg_reset(z);
2389 }
2390 }
2391 }
2392 return 1;
2393 }
2394 } else {
2395 if (z->scan_n == 1) {
2396 int i,j;
2397 int n = z->order[0];
2398 // non-interleaved data, we just need to process one block at a time,
2399 // in trivial scanline order
2400 // number of blocks to do just depends on how many actual "pixels" this
2401 // component has, independent of interleaved MCU blocking and such
2402 int w = (z->img_comp[n].x+7) >> 3;
2403 int h = (z->img_comp[n].y+7) >> 3;
2404 for (j=0; j < h; ++j) {
2405 for (i=0; i < w; ++i) {
2406 short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
2407 if (z->spec_start == 0) {
2408 if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
2409 return 0;
2410 } else {
2411 int ha = z->img_comp[n].ha;
2412 if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
2413 return 0;
2414 }
2415 // every data block is an MCU, so countdown the restart interval
2416 if (--z->todo <= 0) {
2417 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2418 if (!STBI__RESTART(z->marker)) return 1;
2419 stbi__jpeg_reset(z);
2420 }
2421 }
2422 }
2423 return 1;
2424 } else { // interleaved
2425 int i,j,k,x,y;
2426 for (j=0; j < z->img_mcu_y; ++j) {
2427 for (i=0; i < z->img_mcu_x; ++i) {
2428 // scan an interleaved mcu... process scan_n components in order
2429 for (k=0; k < z->scan_n; ++k) {
2430 int n = z->order[k];
2431 // scan out an mcu's worth of this component; that's just determined
2432 // by the basic H and V specified for the component
2433 for (y=0; y < z->img_comp[n].v; ++y) {
2434 for (x=0; x < z->img_comp[n].h; ++x) {
2435 int x2 = (i*z->img_comp[n].h + x);
2436 int y2 = (j*z->img_comp[n].v + y);
2437 short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
2438 if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
2439 return 0;
2440 }
2441 }
2442 }
2443 // after all interleaved components, that's an interleaved MCU,
2444 // so now count down the restart interval
2445 if (--z->todo <= 0) {
2446 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2447 if (!STBI__RESTART(z->marker)) return 1;
2448 stbi__jpeg_reset(z);
2449 }
2450 }
2451 }
2452 return 1;
2453 }
2454 }
2455}
2456
2457static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant)
2458{
2459 int i;
2460 for (i=0; i < 64; ++i)
2461 data[i] *= dequant[i];
2462}
2463
2464static void stbi__jpeg_finish(stbi__jpeg *z)
2465{
2466 if (z->progressive) {
2467 // dequantize and idct the data
2468 int i,j,n;
2469 for (n=0; n < z->s->img_n; ++n) {
2470 int w = (z->img_comp[n].x+7) >> 3;
2471 int h = (z->img_comp[n].y+7) >> 3;
2472 for (j=0; j < h; ++j) {
2473 for (i=0; i < w; ++i) {
2474 short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
2475 stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
2476 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
2477 }
2478 }
2479 }
2480 }
2481}
2482
2483static int stbi__process_marker(stbi__jpeg *z, int m)
2484{
2485 int L;
2486 switch (m) {
2487 case STBI__MARKER_none: // no marker found
2488 return stbi__err("expected marker","Corrupt JPEG");
2489
2490 case 0xDD: // DRI - specify restart interval
2491 if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG");
2492 z->restart_interval = stbi__get16be(z->s);
2493 return 1;
2494
2495 case 0xDB: // DQT - define quantization table
2496 L = stbi__get16be(z->s)-2;
2497 while (L > 0) {
2498 int q = stbi__get8(z->s);
2499 int p = q >> 4;
2500 int t = q & 15,i;
2501 if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG");
2502 if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG");
2503 for (i=0; i < 64; ++i)
2504 z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s);
2505 L -= 65;
2506 }
2507 return L==0;
2508
2509 case 0xC4: // DHT - define huffman table
2510 L = stbi__get16be(z->s)-2;
2511 while (L > 0) {
2512 stbi_uc *v;
2513 int sizes[16],i,n=0;
2514 int q = stbi__get8(z->s);
2515 int tc = q >> 4;
2516 int th = q & 15;
2517 if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");
2518 for (i=0; i < 16; ++i) {
2519 sizes[i] = stbi__get8(z->s);
2520 n += sizes[i];
2521 }
2522 L -= 17;
2523 if (tc == 0) {
2524 if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
2525 v = z->huff_dc[th].values;
2526 } else {
2527 if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;
2528 v = z->huff_ac[th].values;
2529 }
2530 for (i=0; i < n; ++i)
2531 v[i] = stbi__get8(z->s);
2532 if (tc != 0)
2533 stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
2534 L -= n;
2535 }
2536 return L==0;
2537 }
2538 // check for comment block or APP blocks
2539 if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
2540 stbi__skip(z->s, stbi__get16be(z->s)-2);
2541 return 1;
2542 }
2543 return 0;
2544}
2545
2546// after we see SOS
2547static int stbi__process_scan_header(stbi__jpeg *z)
2548{
2549 int i;
2550 int Ls = stbi__get16be(z->s);
2551 z->scan_n = stbi__get8(z->s);
2552 if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG");
2553 if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG");
2554 for (i=0; i < z->scan_n; ++i) {
2555 int id = stbi__get8(z->s), which;
2556 int q = stbi__get8(z->s);
2557 for (which = 0; which < z->s->img_n; ++which)
2558 if (z->img_comp[which].id == id)
2559 break;
2560 if (which == z->s->img_n) return 0; // no match
2561 z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG");
2562 z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG");
2563 z->order[i] = which;
2564 }
2565
2566 {
2567 int aa;
2568 z->spec_start = stbi__get8(z->s);
2569 z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
2570 aa = stbi__get8(z->s);
2571 z->succ_high = (aa >> 4);
2572 z->succ_low = (aa & 15);
2573 if (z->progressive) {
2574 if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
2575 return stbi__err("bad SOS", "Corrupt JPEG");
2576 } else {
2577 if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG");
2578 if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG");
2579 z->spec_end = 63;
2580 }
2581 }
2582
2583 return 1;
2584}
2585
2586static int stbi__process_frame_header(stbi__jpeg *z, int scan)
2587{
2588 stbi__context *s = z->s;
2589 int Lf,p,i,q, h_max=1,v_max=1,c;
2590 Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG
2591 p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
2592 s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
2593 s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires
2594 c = stbi__get8(s);
2595 if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires
2596 s->img_n = c;
2597 for (i=0; i < c; ++i) {
2598 z->img_comp[i].data = NULL;
2599 z->img_comp[i].linebuf = NULL;
2600 }
2601
2602 if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
2603
2604 for (i=0; i < s->img_n; ++i) {
2605 z->img_comp[i].id = stbi__get8(s);
2606 if (z->img_comp[i].id != i+1) // JFIF requires
2607 if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!
2608 return stbi__err("bad component ID","Corrupt JPEG");
2609 q = stbi__get8(s);
2610 z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
2611 z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
2612 z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
2613 }
2614
2615 if (scan != STBI__SCAN_load) return 1;
2616
2617 if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
2618
2619 for (i=0; i < s->img_n; ++i) {
2620 if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
2621 if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
2622 }
2623
2624 // compute interleaved mcu info
2625 z->img_h_max = h_max;
2626 z->img_v_max = v_max;
2627 z->img_mcu_w = h_max * 8;
2628 z->img_mcu_h = v_max * 8;
2629 z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
2630 z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
2631
2632 for (i=0; i < s->img_n; ++i) {
2633 // number of effective pixels (e.g. for non-interleaved MCU)
2634 z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
2635 z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
2636 // to simplify generation, we'll allocate enough memory to decode
2637 // the bogus oversized data from using interleaved MCUs and their
2638 // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
2639 // discard the extra data until colorspace conversion
2640 z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
2641 z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
2642 z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15);
2643
2644 if (z->img_comp[i].raw_data == NULL) {
2645 for(--i; i >= 0; --i) {
2646 STBI_FREE(z->img_comp[i].raw_data);
2647 z->img_comp[i].data = NULL;
2648 }
2649 return stbi__err("outofmem", "Out of memory");
2650 }
2651 // align blocks for idct using mmx/sse
2652 z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
2653 z->img_comp[i].linebuf = NULL;
2654 if (z->progressive) {
2655 z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3;
2656 z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3;
2657 z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15);
2658 z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);
2659 } else {
2660 z->img_comp[i].coeff = 0;
2661 z->img_comp[i].raw_coeff = 0;
2662 }
2663 }
2664
2665 return 1;
2666}
2667
2668// use comparisons since in some cases we handle more than one case (e.g. SOF)
2669#define stbi__DNL(x) ((x) == 0xdc)
2670#define stbi__SOI(x) ((x) == 0xd8)
2671#define stbi__EOI(x) ((x) == 0xd9)
2672#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
2673#define stbi__SOS(x) ((x) == 0xda)
2674
2675#define stbi__SOF_progressive(x) ((x) == 0xc2)
2676
2677static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
2678{
2679 int m;
2680 z->marker = STBI__MARKER_none; // initialize cached marker to empty
2681 m = stbi__get_marker(z);
2682 if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");
2683 if (scan == STBI__SCAN_type) return 1;
2684 m = stbi__get_marker(z);
2685 while (!stbi__SOF(m)) {
2686 if (!stbi__process_marker(z,m)) return 0;
2687 m = stbi__get_marker(z);
2688 while (m == STBI__MARKER_none) {
2689 // some files have extra padding after their blocks, so ok, we'll scan
2690 if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
2691 m = stbi__get_marker(z);
2692 }
2693 }
2694 z->progressive = stbi__SOF_progressive(m);
2695 if (!stbi__process_frame_header(z, scan)) return 0;
2696 return 1;
2697}
2698
2699// decode image to YCbCr format
2700static int stbi__decode_jpeg_image(stbi__jpeg *j)
2701{
2702 int m;
2703 j->restart_interval = 0;
2704 if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
2705 m = stbi__get_marker(j);
2706 while (!stbi__EOI(m)) {
2707 if (stbi__SOS(m)) {
2708 if (!stbi__process_scan_header(j)) return 0;
2709 if (!stbi__parse_entropy_coded_data(j)) return 0;
2710 if (j->marker == STBI__MARKER_none ) {
2711 // handle 0s at the end of image data from IP Kamera 9060
2712 while (!stbi__at_eof(j->s)) {
2713 int x = stbi__get8(j->s);
2714 if (x == 255) {
2715 j->marker = stbi__get8(j->s);
2716 break;
2717 } else if (x != 0) {
2718 return stbi__err("junk before marker", "Corrupt JPEG");
2719 }
2720 }
2721 // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
2722 }
2723 } else {
2724 if (!stbi__process_marker(j, m)) return 0;
2725 }
2726 m = stbi__get_marker(j);
2727 }
2728 if (j->progressive)
2729 stbi__jpeg_finish(j);
2730 return 1;
2731}
2732
2733// static jfif-centered resampling (across block boundaries)
2734
2735typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,
2736 int w, int hs);
2737
2738#define stbi__div4(x) ((stbi_uc) ((x) >> 2))
2739
2740static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2741{
2742 STBI_NOTUSED(out);
2743 STBI_NOTUSED(in_far);
2744 STBI_NOTUSED(w);
2745 STBI_NOTUSED(hs);
2746 return in_near;
2747}
2748
2749static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2750{
2751 // need to generate two samples vertically for every one in input
2752 int i;
2753 STBI_NOTUSED(hs);
2754 for (i=0; i < w; ++i)
2755 out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);
2756 return out;
2757}
2758
2759static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2760{
2761 // need to generate two samples horizontally for every one in input
2762 int i;
2763 stbi_uc *input = in_near;
2764
2765 if (w == 1) {
2766 // if only one sample, can't do any interpolation
2767 out[0] = out[1] = input[0];
2768 return out;
2769 }
2770
2771 out[0] = input[0];
2772 out[1] = stbi__div4(input[0]*3 + input[1] + 2);
2773 for (i=1; i < w-1; ++i) {
2774 int n = 3*input[i]+2;
2775 out[i*2+0] = stbi__div4(n+input[i-1]);
2776 out[i*2+1] = stbi__div4(n+input[i+1]);
2777 }
2778 out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);
2779 out[i*2+1] = input[w-1];
2780
2781 STBI_NOTUSED(in_far);
2782 STBI_NOTUSED(hs);
2783
2784 return out;
2785}
2786
2787#define stbi__div16(x) ((stbi_uc) ((x) >> 4))
2788
2789static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2790{
2791 // need to generate 2x2 samples for every one in input
2792 int i,t0,t1;
2793 if (w == 1) {
2794 out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
2795 return out;
2796 }
2797
2798 t1 = 3*in_near[0] + in_far[0];
2799 out[0] = stbi__div4(t1+2);
2800 for (i=1; i < w; ++i) {
2801 t0 = t1;
2802 t1 = 3*in_near[i]+in_far[i];
2803 out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
2804 out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
2805 }
2806 out[w*2-1] = stbi__div4(t1+2);
2807
2808 STBI_NOTUSED(hs);
2809
2810 return out;
2811}
2812
2813#if defined(STBI_SSE2) || defined(STBI_NEON)
2814static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2815{
2816 // need to generate 2x2 samples for every one in input
2817 int i=0,t0,t1;
2818
2819 if (w == 1) {
2820 out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
2821 return out;
2822 }
2823
2824 t1 = 3*in_near[0] + in_far[0];
2825 // process groups of 8 pixels for as long as we can.
2826 // note we can't handle the last pixel in a row in this loop
2827 // because we need to handle the filter boundary conditions.
2828 for (; i < ((w-1) & ~7); i += 8) {
2829#if defined(STBI_SSE2)
2830 // load and perform the vertical filtering pass
2831 // this uses 3*x + y = 4*x + (y - x)
2832 __m128i zero = _mm_setzero_si128();
2833 __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));
2834 __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));
2835 __m128i farw = _mm_unpacklo_epi8(farb, zero);
2836 __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
2837 __m128i diff = _mm_sub_epi16(farw, nearw);
2838 __m128i nears = _mm_slli_epi16(nearw, 2);
2839 __m128i curr = _mm_add_epi16(nears, diff); // current row
2840
2841 // horizontal filter works the same based on shifted vers of current
2842 // row. "prev" is current row shifted right by 1 pixel; we need to
2843 // insert the previous pixel value (from t1).
2844 // "next" is current row shifted left by 1 pixel, with first pixel
2845 // of next block of 8 pixels added in.
2846 __m128i prv0 = _mm_slli_si128(curr, 2);
2847 __m128i nxt0 = _mm_srli_si128(curr, 2);
2848 __m128i prev = _mm_insert_epi16(prv0, t1, 0);
2849 __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);
2850
2851 // horizontal filter, polyphase implementation since it's convenient:
2852 // even pixels = 3*cur + prev = cur*4 + (prev - cur)
2853 // odd pixels = 3*cur + next = cur*4 + (next - cur)
2854 // note the shared term.
2855 __m128i bias = _mm_set1_epi16(8);
2856 __m128i curs = _mm_slli_epi16(curr, 2);
2857 __m128i prvd = _mm_sub_epi16(prev, curr);
2858 __m128i nxtd = _mm_sub_epi16(next, curr);
2859 __m128i curb = _mm_add_epi16(curs, bias);
2860 __m128i even = _mm_add_epi16(prvd, curb);
2861 __m128i odd = _mm_add_epi16(nxtd, curb);
2862
2863 // interleave even and odd pixels, then undo scaling.
2864 __m128i int0 = _mm_unpacklo_epi16(even, odd);
2865 __m128i int1 = _mm_unpackhi_epi16(even, odd);
2866 __m128i de0 = _mm_srli_epi16(int0, 4);
2867 __m128i de1 = _mm_srli_epi16(int1, 4);
2868
2869 // pack and write output
2870 __m128i outv = _mm_packus_epi16(de0, de1);
2871 _mm_storeu_si128((__m128i *) (out + i*2), outv);
2872#elif defined(STBI_NEON)
2873 // load and perform the vertical filtering pass
2874 // this uses 3*x + y = 4*x + (y - x)
2875 uint8x8_t farb = vld1_u8(in_far + i);
2876 uint8x8_t nearb = vld1_u8(in_near + i);
2877 int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
2878 int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
2879 int16x8_t curr = vaddq_s16(nears, diff); // current row
2880
2881 // horizontal filter works the same based on shifted vers of current
2882 // row. "prev" is current row shifted right by 1 pixel; we need to
2883 // insert the previous pixel value (from t1).
2884 // "next" is current row shifted left by 1 pixel, with first pixel
2885 // of next block of 8 pixels added in.
2886 int16x8_t prv0 = vextq_s16(curr, curr, 7);
2887 int16x8_t nxt0 = vextq_s16(curr, curr, 1);
2888 int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
2889 int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);
2890
2891 // horizontal filter, polyphase implementation since it's convenient:
2892 // even pixels = 3*cur + prev = cur*4 + (prev - cur)
2893 // odd pixels = 3*cur + next = cur*4 + (next - cur)
2894 // note the shared term.
2895 int16x8_t curs = vshlq_n_s16(curr, 2);
2896 int16x8_t prvd = vsubq_s16(prev, curr);
2897 int16x8_t nxtd = vsubq_s16(next, curr);
2898 int16x8_t even = vaddq_s16(curs, prvd);
2899 int16x8_t odd = vaddq_s16(curs, nxtd);
2900
2901 // undo scaling and round, then store with even/odd phases interleaved
2902 uint8x8x2_t o;
2903 o.val[0] = vqrshrun_n_s16(even, 4);
2904 o.val[1] = vqrshrun_n_s16(odd, 4);
2905 vst2_u8(out + i*2, o);
2906#endif
2907
2908 // "previous" value for next iter
2909 t1 = 3*in_near[i+7] + in_far[i+7];
2910 }
2911
2912 t0 = t1;
2913 t1 = 3*in_near[i] + in_far[i];
2914 out[i*2] = stbi__div16(3*t1 + t0 + 8);
2915
2916 for (++i; i < w; ++i) {
2917 t0 = t1;
2918 t1 = 3*in_near[i]+in_far[i];
2919 out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
2920 out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
2921 }
2922 out[w*2-1] = stbi__div4(t1+2);
2923
2924 STBI_NOTUSED(hs);
2925
2926 return out;
2927}
2928#endif
2929
2930static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
2931{
2932 // resample with nearest-neighbor
2933 int i,j;
2934 STBI_NOTUSED(in_far);
2935 for (i=0; i < w; ++i)
2936 for (j=0; j < hs; ++j)
2937 out[i*hs+j] = in_near[i];
2938 return out;
2939}
2940
2941#ifdef STBI_JPEG_OLD
2942// this is the same YCbCr-to-RGB calculation that stb_image has used
2943// historically before the algorithm changes in 1.49
2944#define float2fixed(x) ((int) ((x) * 65536 + 0.5))
2945static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
2946{
2947 int i;
2948 for (i=0; i < count; ++i) {
2949 int y_fixed = (y[i] << 16) + 32768; // rounding
2950 int r,g,b;
2951 int cr = pcr[i] - 128;
2952 int cb = pcb[i] - 128;
2953 r = y_fixed + cr*float2fixed(1.40200f);
2954 g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f);
2955 b = y_fixed + cb*float2fixed(1.77200f);
2956 r >>= 16;
2957 g >>= 16;
2958 b >>= 16;
2959 if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
2960 if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
2961 if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
2962 out[0] = (stbi_uc)r;
2963 out[1] = (stbi_uc)g;
2964 out[2] = (stbi_uc)b;
2965 out[3] = 255;
2966 out += step;
2967 }
2968}
2969#else
2970// this is a reduced-precision calculation of YCbCr-to-RGB introduced
2971// to make sure the code produces the same results in both SIMD and scalar
2972#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
2973static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
2974{
2975 int i;
2976 for (i=0; i < count; ++i) {
2977 int y_fixed = (y[i] << 20) + (1<<19); // rounding
2978 int r,g,b;
2979 int cr = pcr[i] - 128;
2980 int cb = pcb[i] - 128;
2981 r = y_fixed + cr* float2fixed(1.40200f);
2982 g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000);
2983 b = y_fixed + cb* float2fixed(1.77200f);
2984 r >>= 20;
2985 g >>= 20;
2986 b >>= 20;
2987 if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
2988 if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
2989 if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
2990 out[0] = (stbi_uc)r;
2991 out[1] = (stbi_uc)g;
2992 out[2] = (stbi_uc)b;
2993 out[3] = 255;
2994 out += step;
2995 }
2996}
2997#endif
2998
2999#if defined(STBI_SSE2) || defined(STBI_NEON)
3000static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)
3001{
3002 int i = 0;
3003
3004#ifdef STBI_SSE2
3005 // step == 3 is pretty ugly on the final interleave, and i'm not convinced
3006 // it's useful in practice (you wouldn't use it for textures, for example).
3007 // so just accelerate step == 4 case.
3008 if (step == 4) {
3009 // this is a fairly straightforward implementation and not super-optimized.
3010 __m128i signflip = _mm_set1_epi8(-0x80);
3011 __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
3012 __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
3013 __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
3014 __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
3015 __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
3016 __m128i xw = _mm_set1_epi16(255); // alpha channel
3017
3018 for (; i+7 < count; i += 8) {
3019 // load
3020 __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
3021 __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
3022 __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
3023 __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
3024 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
3025
3026 // unpack to short (and left-shift cr, cb by 8)
3027 __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
3028 __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
3029 __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
3030
3031 // color transform
3032 __m128i yws = _mm_srli_epi16(yw, 4);
3033 __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
3034 __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
3035 __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
3036 __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
3037 __m128i rws = _mm_add_epi16(cr0, yws);
3038 __m128i gwt = _mm_add_epi16(cb0, yws);
3039 __m128i bws = _mm_add_epi16(yws, cb1);
3040 __m128i gws = _mm_add_epi16(gwt, cr1);
3041
3042 // descale
3043 __m128i rw = _mm_srai_epi16(rws, 4);
3044 __m128i bw = _mm_srai_epi16(bws, 4);
3045 __m128i gw = _mm_srai_epi16(gws, 4);
3046
3047 // back to byte, set up for transpose
3048 __m128i brb = _mm_packus_epi16(rw, bw);
3049 __m128i gxb = _mm_packus_epi16(gw, xw);
3050
3051 // transpose to interleave channels
3052 __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
3053 __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
3054 __m128i o0 = _mm_unpacklo_epi16(t0, t1);
3055 __m128i o1 = _mm_unpackhi_epi16(t0, t1);
3056
3057 // store
3058 _mm_storeu_si128((__m128i *) (out + 0), o0);
3059 _mm_storeu_si128((__m128i *) (out + 16), o1);
3060 out += 32;
3061 }
3062 }
3063#endif
3064
3065#ifdef STBI_NEON
3066 // in this version, step=3 support would be easy to add. but is there demand?
3067 if (step == 4) {
3068 // this is a fairly straightforward implementation and not super-optimized.
3069 uint8x8_t signflip = vdup_n_u8(0x80);
3070 int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f));
3071 int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));
3072 int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));
3073 int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f));
3074
3075 for (; i+7 < count; i += 8) {
3076 // load
3077 uint8x8_t y_bytes = vld1_u8(y + i);
3078 uint8x8_t cr_bytes = vld1_u8(pcr + i);
3079 uint8x8_t cb_bytes = vld1_u8(pcb + i);
3080 int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
3081 int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
3082
3083 // expand to s16
3084 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
3085 int16x8_t crw = vshll_n_s8(cr_biased, 7);
3086 int16x8_t cbw = vshll_n_s8(cb_biased, 7);
3087
3088 // color transform
3089 int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
3090 int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
3091 int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
3092 int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
3093 int16x8_t rws = vaddq_s16(yws, cr0);
3094 int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
3095 int16x8_t bws = vaddq_s16(yws, cb1);
3096
3097 // undo scaling, round, convert to byte
3098 uint8x8x4_t o;
3099 o.val[0] = vqrshrun_n_s16(rws, 4);
3100 o.val[1] = vqrshrun_n_s16(gws, 4);
3101 o.val[2] = vqrshrun_n_s16(bws, 4);
3102 o.val[3] = vdup_n_u8(255);
3103
3104 // store, interleaving r/g/b/a
3105 vst4_u8(out, o);
3106 out += 8*4;
3107 }
3108 }
3109#endif
3110
3111 for (; i < count; ++i) {
3112 int y_fixed = (y[i] << 20) + (1<<19); // rounding
3113 int r,g,b;
3114 int cr = pcr[i] - 128;
3115 int cb = pcb[i] - 128;
3116 r = y_fixed + cr* float2fixed(1.40200f);
3117 g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000);
3118 b = y_fixed + cb* float2fixed(1.77200f);
3119 r >>= 20;
3120 g >>= 20;
3121 b >>= 20;
3122 if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
3123 if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
3124 if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
3125 out[0] = (stbi_uc)r;
3126 out[1] = (stbi_uc)g;
3127 out[2] = (stbi_uc)b;
3128 out[3] = 255;
3129 out += step;
3130 }
3131}
3132#endif
3133
3134// set up the kernels
3135static void stbi__setup_jpeg(stbi__jpeg *j)
3136{
3137 j->idct_block_kernel = stbi__idct_block;
3138 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
3139 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
3140
3141#ifdef STBI_SSE2
3142 if (stbi__sse2_available()) {
3143 j->idct_block_kernel = stbi__idct_simd;
3144 #ifndef STBI_JPEG_OLD
3145 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
3146 #endif
3147 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
3148 }
3149#endif
3150
3151#ifdef STBI_NEON
3152 j->idct_block_kernel = stbi__idct_simd;
3153 #ifndef STBI_JPEG_OLD
3154 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
3155 #endif
3156 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
3157#endif
3158}
3159
3160// clean up the temporary component buffers
3161static void stbi__cleanup_jpeg(stbi__jpeg *j)
3162{
3163 int i;
3164 for (i=0; i < j->s->img_n; ++i) {
3165 if (j->img_comp[i].raw_data) {
3166 STBI_FREE(j->img_comp[i].raw_data);
3167 j->img_comp[i].raw_data = NULL;
3168 j->img_comp[i].data = NULL;
3169 }
3170 if (j->img_comp[i].raw_coeff) {
3171 STBI_FREE(j->img_comp[i].raw_coeff);
3172 j->img_comp[i].raw_coeff = 0;
3173 j->img_comp[i].coeff = 0;
3174 }
3175 if (j->img_comp[i].linebuf) {
3176 STBI_FREE(j->img_comp[i].linebuf);
3177 j->img_comp[i].linebuf = NULL;
3178 }
3179 }
3180}
3181
3182typedef struct
3183{
3184 resample_row_func resample;
3185 stbi_uc *line0,*line1;
3186 int hs,vs; // expansion factor in each axis
3187 int w_lores; // horizontal pixels pre-expansion
3188 int ystep; // how far through vertical expansion we are
3189 int ypos; // which pre-expansion row we're on
3190} stbi__resample;
3191
3192static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)
3193{
3194 int n, decode_n;
3195 z->s->img_n = 0; // make stbi__cleanup_jpeg safe
3196
3197 // validate req_comp
3198 if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
3199
3200 // load a jpeg image from whichever source, but leave in YCbCr format
3201 if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }
3202
3203 // determine actual number of components to generate
3204 n = req_comp ? req_comp : z->s->img_n;
3205
3206 if (z->s->img_n == 3 && n < 3)
3207 decode_n = 1;
3208 else
3209 decode_n = z->s->img_n;
3210
3211 // resample and color-convert
3212 {
3213 int k;
3214 unsigned int i,j;
3215 stbi_uc *output;
3216 stbi_uc *coutput[4];
3217
3218 stbi__resample res_comp[4];
3219
3220 for (k=0; k < decode_n; ++k) {
3221 stbi__resample *r = &res_comp[k];
3222
3223 // allocate line buffer big enough for upsampling off the edges
3224 // with upsample factor of 4
3225 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);
3226 if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
3227
3228 r->hs = z->img_h_max / z->img_comp[k].h;
3229 r->vs = z->img_v_max / z->img_comp[k].v;
3230 r->ystep = r->vs >> 1;
3231 r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
3232 r->ypos = 0;
3233 r->line0 = r->line1 = z->img_comp[k].data;
3234
3235 if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
3236 else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
3237 else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
3238 else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
3239 else r->resample = stbi__resample_row_generic;
3240 }
3241
3242 // can't error after this so, this is safe
3243 output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1);
3244 if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
3245
3246 // now go ahead and resample
3247 for (j=0; j < z->s->img_y; ++j) {
3248 stbi_uc *out = output + n * z->s->img_x * j;
3249 for (k=0; k < decode_n; ++k) {
3250 stbi__resample *r = &res_comp[k];
3251 int y_bot = r->ystep >= (r->vs >> 1);
3252 coutput[k] = r->resample(z->img_comp[k].linebuf,
3253 y_bot ? r->line1 : r->line0,
3254 y_bot ? r->line0 : r->line1,
3255 r->w_lores, r->hs);
3256 if (++r->ystep >= r->vs) {
3257 r->ystep = 0;
3258 r->line0 = r->line1;
3259 if (++r->ypos < z->img_comp[k].y)
3260 r->line1 += z->img_comp[k].w2;
3261 }
3262 }
3263 if (n >= 3) {
3264 stbi_uc *y = coutput[0];
3265 if (z->s->img_n == 3) {
3266 z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
3267 } else
3268 for (i=0; i < z->s->img_x; ++i) {
3269 out[0] = out[1] = out[2] = y[i];
3270 out[3] = 255; // not used if n==3
3271 out += n;
3272 }
3273 } else {
3274 stbi_uc *y = coutput[0];
3275 if (n == 1)
3276 for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
3277 else
3278 for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255;
3279 }
3280 }
3281 stbi__cleanup_jpeg(z);
3282 *out_x = z->s->img_x;
3283 *out_y = z->s->img_y;
3284 if (comp) *comp = z->s->img_n; // report original components, not output
3285 return output;
3286 }
3287}
3288
3289static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
3290{
3291 stbi__jpeg j;
3292 j.s = s;
3293 stbi__setup_jpeg(&j);
3294 return load_jpeg_image(&j, x,y,comp,req_comp);
3295}
3296
3297static int stbi__jpeg_test(stbi__context *s)
3298{
3299 int r;
3300 stbi__jpeg j;
3301 j.s = s;
3302 stbi__setup_jpeg(&j);
3303 r = stbi__decode_jpeg_header(&j, STBI__SCAN_type);
3304 stbi__rewind(s);
3305 return r;
3306}
3307
3308static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
3309{
3310 if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
3311 stbi__rewind( j->s );
3312 return 0;
3313 }
3314 if (x) *x = j->s->img_x;
3315 if (y) *y = j->s->img_y;
3316 if (comp) *comp = j->s->img_n;
3317 return 1;
3318}
3319
3320static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
3321{
3322 stbi__jpeg j;
3323 j.s = s;
3324 return stbi__jpeg_info_raw(&j, x, y, comp);
3325}
3326#endif
3327
3328// public domain zlib decode v0.2 Sean Barrett 2006-11-18
3329// simple implementation
3330// - all input must be provided in an upfront buffer
3331// - all output is written to a single output buffer (can malloc/realloc)
3332// performance
3333// - fast huffman
3334
3335#ifndef STBI_NO_ZLIB
3336
3337// fast-way is faster to check than jpeg huffman, but slow way is slower
3338#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
3339#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
3340
3341// zlib-style huffman encoding
3342// (jpegs packs from left, zlib from right, so can't share code)
3343typedef struct
3344{
3345 stbi__uint16 fast[1 << STBI__ZFAST_BITS];
3346 stbi__uint16 firstcode[16];
3347 int maxcode[17];
3348 stbi__uint16 firstsymbol[16];
3349 stbi_uc size[288];
3350 stbi__uint16 value[288];
3351} stbi__zhuffman;
3352
3353stbi_inline static int stbi__bitreverse16(int n)
3354{
3355 n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
3356 n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
3357 n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
3358 n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
3359 return n;
3360}
3361
3362stbi_inline static int stbi__bit_reverse(int v, int bits)
3363{
3364 STBI_ASSERT(bits <= 16);
3365 // to bit reverse n bits, reverse 16 and shift
3366 // e.g. 11 bits, bit reverse and shift away 5
3367 return stbi__bitreverse16(v) >> (16-bits);
3368}
3369
3370static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
3371{
3372 int i,k=0;
3373 int code, next_code[16], sizes[17];
3374
3375 // DEFLATE spec for generating codes
3376 memset(sizes, 0, sizeof(sizes));
3377 memset(z->fast, 0, sizeof(z->fast));
3378 for (i=0; i < num; ++i)
3379 ++sizes[sizelist[i]];
3380 sizes[0] = 0;
3381 for (i=1; i < 16; ++i)
3382 STBI_ASSERT(sizes[i] <= (1 << i));
3383 code = 0;
3384 for (i=1; i < 16; ++i) {
3385 next_code[i] = code;
3386 z->firstcode[i] = (stbi__uint16) code;
3387 z->firstsymbol[i] = (stbi__uint16) k;
3388 code = (code + sizes[i]);
3389 if (sizes[i])
3390 if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG");
3391 z->maxcode[i] = code << (16-i); // preshift for inner loop
3392 code <<= 1;
3393 k += sizes[i];
3394 }
3395 z->maxcode[16] = 0x10000; // sentinel
3396 for (i=0; i < num; ++i) {
3397 int s = sizelist[i];
3398 if (s) {
3399 int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
3400 stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
3401 z->size [c] = (stbi_uc ) s;
3402 z->value[c] = (stbi__uint16) i;
3403 if (s <= STBI__ZFAST_BITS) {
3404 int k = stbi__bit_reverse(next_code[s],s);
3405 while (k < (1 << STBI__ZFAST_BITS)) {
3406 z->fast[k] = fastv;
3407 k += (1 << s);
3408 }
3409 }
3410 ++next_code[s];
3411 }
3412 }
3413 return 1;
3414}
3415
3416// zlib-from-memory implementation for PNG reading
3417// because PNG allows splitting the zlib stream arbitrarily,
3418// and it's annoying structurally to have PNG call ZLIB call PNG,
3419// we require PNG read all the IDATs and combine them into a single
3420// memory buffer
3421
3422typedef struct
3423{
3424 stbi_uc *zbuffer, *zbuffer_end;
3425 int num_bits;
3426 stbi__uint32 code_buffer;
3427
3428 char *zout;
3429 char *zout_start;
3430 char *zout_end;
3431 int z_expandable;
3432
3433 stbi__zhuffman z_length, z_distance;
3434} stbi__zbuf;
3435
3436stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
3437{
3438 if (z->zbuffer >= z->zbuffer_end) return 0;
3439 return *z->zbuffer++;
3440}
3441
3442static void stbi__fill_bits(stbi__zbuf *z)
3443{
3444 do {
3445 STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
3446 z->code_buffer |= stbi__zget8(z) << z->num_bits;
3447 z->num_bits += 8;
3448 } while (z->num_bits <= 24);
3449}
3450
3451stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
3452{
3453 unsigned int k;
3454 if (z->num_bits < n) stbi__fill_bits(z);
3455 k = z->code_buffer & ((1 << n) - 1);
3456 z->code_buffer >>= n;
3457 z->num_bits -= n;
3458 return k;
3459}
3460
3461static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
3462{
3463 int b,s,k;
3464 // not resolved by fast table, so compute it the slow way
3465 // use jpeg approach, which requires MSbits at top
3466 k = stbi__bit_reverse(a->code_buffer, 16);
3467 for (s=STBI__ZFAST_BITS+1; ; ++s)
3468 if (k < z->maxcode[s])
3469 break;
3470 if (s == 16) return -1; // invalid code!
3471 // code size is s, so:
3472 b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
3473 STBI_ASSERT(z->size[b] == s);
3474 a->code_buffer >>= s;
3475 a->num_bits -= s;
3476 return z->value[b];
3477}
3478
3479stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
3480{
3481 int b,s;
3482 if (a->num_bits < 16) stbi__fill_bits(a);
3483 b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
3484 if (b) {
3485 s = b >> 9;
3486 a->code_buffer >>= s;
3487 a->num_bits -= s;
3488 return b & 511;
3489 }
3490 return stbi__zhuffman_decode_slowpath(a, z);
3491}
3492
3493static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
3494{
3495 char *q;
3496 int cur, limit;
3497 z->zout = zout;
3498 if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
3499 cur = (int) (z->zout - z->zout_start);
3500 limit = (int) (z->zout_end - z->zout_start);
3501 while (cur + n > limit)
3502 limit *= 2;
3503 q = (char *) STBI_REALLOC(z->zout_start, limit);
3504 if (q == NULL) return stbi__err("outofmem", "Out of memory");
3505 z->zout_start = q;
3506 z->zout = q + cur;
3507 z->zout_end = q + limit;
3508 return 1;
3509}
3510
3511static int stbi__zlength_base[31] = {
3512 3,4,5,6,7,8,9,10,11,13,
3513 15,17,19,23,27,31,35,43,51,59,
3514 67,83,99,115,131,163,195,227,258,0,0 };
3515
3516static int stbi__zlength_extra[31]=
3517{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
3518
3519static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
3520257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
3521
3522static int stbi__zdist_extra[32] =
3523{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
3524
3525static int stbi__parse_huffman_block(stbi__zbuf *a)
3526{
3527 char *zout = a->zout;
3528 for(;;) {
3529 int z = stbi__zhuffman_decode(a, &a->z_length);
3530 if (z < 256) {
3531 if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
3532 if (zout >= a->zout_end) {
3533 if (!stbi__zexpand(a, zout, 1)) return 0;
3534 zout = a->zout;
3535 }
3536 *zout++ = (char) z;
3537 } else {
3538 stbi_uc *p;
3539 int len,dist;
3540 if (z == 256) {
3541 a->zout = zout;
3542 return 1;
3543 }
3544 z -= 257;
3545 len = stbi__zlength_base[z];
3546 if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
3547 z = stbi__zhuffman_decode(a, &a->z_distance);
3548 if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
3549 dist = stbi__zdist_base[z];
3550 if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
3551 if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
3552 if (zout + len > a->zout_end) {
3553 if (!stbi__zexpand(a, zout, len)) return 0;
3554 zout = a->zout;
3555 }
3556 p = (stbi_uc *) (zout - dist);
3557 if (dist == 1) { // run of one byte; common in images.
3558 stbi_uc v = *p;
3559 do *zout++ = v; while (--len);
3560 } else {
3561 do *zout++ = *p++; while (--len);
3562 }
3563 }
3564 }
3565}
3566
3567static int stbi__compute_huffman_codes(stbi__zbuf *a)
3568{
3569 static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
3570 stbi__zhuffman z_codelength;
3571 stbi_uc lencodes[286+32+137];//padding for maximum single op
3572 stbi_uc codelength_sizes[19];
3573 int i,n;
3574
3575 int hlit = stbi__zreceive(a,5) + 257;
3576 int hdist = stbi__zreceive(a,5) + 1;
3577 int hclen = stbi__zreceive(a,4) + 4;
3578
3579 memset(codelength_sizes, 0, sizeof(codelength_sizes));
3580 for (i=0; i < hclen; ++i) {
3581 int s = stbi__zreceive(a,3);
3582 codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
3583 }
3584 if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
3585
3586 n = 0;
3587 while (n < hlit + hdist) {
3588 int c = stbi__zhuffman_decode(a, &z_codelength);
3589 STBI_ASSERT(c >= 0 && c < 19);
3590 if (c < 16)
3591 lencodes[n++] = (stbi_uc) c;
3592 else if (c == 16) {
3593 c = stbi__zreceive(a,2)+3;
3594 memset(lencodes+n, lencodes[n-1], c);
3595 n += c;
3596 } else if (c == 17) {
3597 c = stbi__zreceive(a,3)+3;
3598 memset(lencodes+n, 0, c);
3599 n += c;
3600 } else {
3601 STBI_ASSERT(c == 18);
3602 c = stbi__zreceive(a,7)+11;
3603 memset(lencodes+n, 0, c);
3604 n += c;
3605 }
3606 }
3607 if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG");
3608 if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
3609 if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
3610 return 1;
3611}
3612
3613static int stbi__parse_uncomperssed_block(stbi__zbuf *a)
3614{
3615 stbi_uc header[4];
3616 int len,nlen,k;
3617 if (a->num_bits & 7)
3618 stbi__zreceive(a, a->num_bits & 7); // discard
3619 // drain the bit-packed data into header
3620 k = 0;
3621 while (a->num_bits > 0) {
3622 header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
3623 a->code_buffer >>= 8;
3624 a->num_bits -= 8;
3625 }
3626 STBI_ASSERT(a->num_bits == 0);
3627 // now fill header the normal way
3628 while (k < 4)
3629 header[k++] = stbi__zget8(a);
3630 len = header[1] * 256 + header[0];
3631 nlen = header[3] * 256 + header[2];
3632 if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
3633 if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
3634 if (a->zout + len > a->zout_end)
3635 if (!stbi__zexpand(a, a->zout, len)) return 0;
3636 memcpy(a->zout, a->zbuffer, len);
3637 a->zbuffer += len;
3638 a->zout += len;
3639 return 1;
3640}
3641
3642static int stbi__parse_zlib_header(stbi__zbuf *a)
3643{
3644 int cmf = stbi__zget8(a);
3645 int cm = cmf & 15;
3646 /* int cinfo = cmf >> 4; */
3647 int flg = stbi__zget8(a);
3648 if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
3649 if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
3650 if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
3651 // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
3652 return 1;
3653}
3654
3655// @TODO: should statically initialize these for optimal thread safety
3656static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32];
3657static void stbi__init_zdefaults(void)
3658{
3659 int i; // use <= to match clearly with spec
3660 for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
3661 for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
3662 for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
3663 for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
3664
3665 for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
3666}
3667
3668static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
3669{
3670 int final, type;
3671 if (parse_header)
3672 if (!stbi__parse_zlib_header(a)) return 0;
3673 a->num_bits = 0;
3674 a->code_buffer = 0;
3675 do {
3676 final = stbi__zreceive(a,1);
3677 type = stbi__zreceive(a,2);
3678 if (type == 0) {
3679 if (!stbi__parse_uncomperssed_block(a)) return 0;
3680 } else if (type == 3) {
3681 return 0;
3682 } else {
3683 if (type == 1) {
3684 // use fixed code lengths
3685 if (!stbi__zdefault_distance[31]) stbi__init_zdefaults();
3686 if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
3687 if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
3688 } else {
3689 if (!stbi__compute_huffman_codes(a)) return 0;
3690 }
3691 if (!stbi__parse_huffman_block(a)) return 0;
3692 }
3693 } while (!final);
3694 return 1;
3695}
3696
3697static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
3698{
3699 a->zout_start = obuf;
3700 a->zout = obuf;
3701 a->zout_end = obuf + olen;
3702 a->z_expandable = exp;
3703
3704 return stbi__parse_zlib(a, parse_header);
3705}
3706
3707STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
3708{
3709 stbi__zbuf a;
3710 char *p = (char *) stbi__malloc(initial_size);
3711 if (p == NULL) return NULL;
3712 a.zbuffer = (stbi_uc *) buffer;
3713 a.zbuffer_end = (stbi_uc *) buffer + len;
3714 if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
3715 if (outlen) *outlen = (int) (a.zout - a.zout_start);
3716 return a.zout_start;
3717 } else {
3718 STBI_FREE(a.zout_start);
3719 return NULL;
3720 }
3721}
3722
3723STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
3724{
3725 return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
3726}
3727
3728STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
3729{
3730 stbi__zbuf a;
3731 char *p = (char *) stbi__malloc(initial_size);
3732 if (p == NULL) return NULL;
3733 a.zbuffer = (stbi_uc *) buffer;
3734 a.zbuffer_end = (stbi_uc *) buffer + len;
3735 if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
3736 if (outlen) *outlen = (int) (a.zout - a.zout_start);
3737 return a.zout_start;
3738 } else {
3739 STBI_FREE(a.zout_start);
3740 return NULL;
3741 }
3742}
3743
3744STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
3745{
3746 stbi__zbuf a;
3747 a.zbuffer = (stbi_uc *) ibuffer;
3748 a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
3749 if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
3750 return (int) (a.zout - a.zout_start);
3751 else
3752 return -1;
3753}
3754
3755STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
3756{
3757 stbi__zbuf a;
3758 char *p = (char *) stbi__malloc(16384);
3759 if (p == NULL) return NULL;
3760 a.zbuffer = (stbi_uc *) buffer;
3761 a.zbuffer_end = (stbi_uc *) buffer+len;
3762 if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
3763 if (outlen) *outlen = (int) (a.zout - a.zout_start);
3764 return a.zout_start;
3765 } else {
3766 STBI_FREE(a.zout_start);
3767 return NULL;
3768 }
3769}
3770
3771STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
3772{
3773 stbi__zbuf a;
3774 a.zbuffer = (stbi_uc *) ibuffer;
3775 a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
3776 if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
3777 return (int) (a.zout - a.zout_start);
3778 else
3779 return -1;
3780}
3781#endif
3782
3783// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
3784// simple implementation
3785// - only 8-bit samples
3786// - no CRC checking
3787// - allocates lots of intermediate memory
3788// - avoids problem of streaming data between subsystems
3789// - avoids explicit window management
3790// performance
3791// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
3792
3793#ifndef STBI_NO_PNG
3794typedef struct
3795{
3796 stbi__uint32 length;
3797 stbi__uint32 type;
3798} stbi__pngchunk;
3799
3800static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
3801{
3802 stbi__pngchunk c;
3803 c.length = stbi__get32be(s);
3804 c.type = stbi__get32be(s);
3805 return c;
3806}
3807
3808static int stbi__check_png_header(stbi__context *s)
3809{
3810 static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
3811 int i;
3812 for (i=0; i < 8; ++i)
3813 if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
3814 return 1;
3815}
3816
3817typedef struct
3818{
3819 stbi__context *s;
3820 stbi_uc *idata, *expanded, *out;
3821} stbi__png;
3822
3823
3824enum {
3825 STBI__F_none=0,
3826 STBI__F_sub=1,
3827 STBI__F_up=2,
3828 STBI__F_avg=3,
3829 STBI__F_paeth=4,
3830 // synthetic filters used for first scanline to avoid needing a dummy row of 0s
3831 STBI__F_avg_first,
3832 STBI__F_paeth_first
3833};
3834
3835static stbi_uc first_row_filter[5] =
3836{
3837 STBI__F_none,
3838 STBI__F_sub,
3839 STBI__F_none,
3840 STBI__F_avg_first,
3841 STBI__F_paeth_first
3842};
3843
3844static int stbi__paeth(int a, int b, int c)
3845{
3846 int p = a + b - c;
3847 int pa = abs(p-a);
3848 int pb = abs(p-b);
3849 int pc = abs(p-c);
3850 if (pa <= pb && pa <= pc) return a;
3851 if (pb <= pc) return b;
3852 return c;
3853}
3854
3855static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
3856
3857// create the png data from post-deflated data
3858static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
3859{
3860 stbi__context *s = a->s;
3861 stbi__uint32 i,j,stride = x*out_n;
3862 stbi__uint32 img_len, img_width_bytes;
3863 int k;
3864 int img_n = s->img_n; // copy it into a local for later
3865
3866 STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
3867 a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into
3868 if (!a->out) return stbi__err("outofmem", "Out of memory");
3869
3870 img_width_bytes = (((img_n * x * depth) + 7) >> 3);
3871 img_len = (img_width_bytes + 1) * y;
3872 if (s->img_x == x && s->img_y == y) {
3873 if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG");
3874 } else { // interlaced:
3875 if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
3876 }
3877
3878 for (j=0; j < y; ++j) {
3879 stbi_uc *cur = a->out + stride*j;
3880 stbi_uc *prior = cur - stride;
3881 int filter = *raw++;
3882 int filter_bytes = img_n;
3883 int width = x;
3884 if (filter > 4)
3885 return stbi__err("invalid filter","Corrupt PNG");
3886
3887 if (depth < 8) {
3888 STBI_ASSERT(img_width_bytes <= x);
3889 cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
3890 filter_bytes = 1;
3891 width = img_width_bytes;
3892 }
3893
3894 // if first row, use special filter that doesn't sample previous row
3895 if (j == 0) filter = first_row_filter[filter];
3896
3897 // handle first byte explicitly
3898 for (k=0; k < filter_bytes; ++k) {
3899 switch (filter) {
3900 case STBI__F_none : cur[k] = raw[k]; break;
3901 case STBI__F_sub : cur[k] = raw[k]; break;
3902 case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
3903 case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
3904 case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
3905 case STBI__F_avg_first : cur[k] = raw[k]; break;
3906 case STBI__F_paeth_first: cur[k] = raw[k]; break;
3907 }
3908 }
3909
3910 if (depth == 8) {
3911 if (img_n != out_n)
3912 cur[img_n] = 255; // first pixel
3913 raw += img_n;
3914 cur += out_n;
3915 prior += out_n;
3916 } else {
3917 raw += 1;
3918 cur += 1;
3919 prior += 1;
3920 }
3921
3922 // this is a little gross, so that we don't switch per-pixel or per-component
3923 if (depth < 8 || img_n == out_n) {
3924 int nk = (width - 1)*img_n;
3925 #define CASE(f) \
3926 case f: \
3927 for (k=0; k < nk; ++k)
3928 switch (filter) {
3929 // "none" filter turns into a memcpy here; make that explicit.
3930 case STBI__F_none: memcpy(cur, raw, nk); break;
3931 CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break;
3932 CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
3933 CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break;
3934 CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break;
3935 CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break;
3936 CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break;
3937 }
3938 #undef CASE
3939 raw += nk;
3940 } else {
3941 STBI_ASSERT(img_n+1 == out_n);
3942 #define CASE(f) \
3943 case f: \
3944 for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
3945 for (k=0; k < img_n; ++k)
3946 switch (filter) {
3947 CASE(STBI__F_none) cur[k] = raw[k]; break;
3948 CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break;
3949 CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
3950 CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break;
3951 CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
3952 CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break;
3953 CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break;
3954 }
3955 #undef CASE
3956 }
3957 }
3958
3959 // we make a separate pass to expand bits to pixels; for performance,
3960 // this could run two scanlines behind the above code, so it won't
3961 // intefere with filtering but will still be in the cache.
3962 if (depth < 8) {
3963 for (j=0; j < y; ++j) {
3964 stbi_uc *cur = a->out + stride*j;
3965 stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
3966 // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
3967 // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
3968 stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
3969
3970 // note that the final byte might overshoot and write more data than desired.
3971 // we can allocate enough data that this never writes out of memory, but it
3972 // could also overwrite the next scanline. can it overwrite non-empty data
3973 // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
3974 // so we need to explicitly clamp the final ones
3975
3976 if (depth == 4) {
3977 for (k=x*img_n; k >= 2; k-=2, ++in) {
3978 *cur++ = scale * ((*in >> 4) );
3979 *cur++ = scale * ((*in ) & 0x0f);
3980 }
3981 if (k > 0) *cur++ = scale * ((*in >> 4) );
3982 } else if (depth == 2) {
3983 for (k=x*img_n; k >= 4; k-=4, ++in) {
3984 *cur++ = scale * ((*in >> 6) );
3985 *cur++ = scale * ((*in >> 4) & 0x03);
3986 *cur++ = scale * ((*in >> 2) & 0x03);
3987 *cur++ = scale * ((*in ) & 0x03);
3988 }
3989 if (k > 0) *cur++ = scale * ((*in >> 6) );
3990 if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
3991 if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
3992 } else if (depth == 1) {
3993 for (k=x*img_n; k >= 8; k-=8, ++in) {
3994 *cur++ = scale * ((*in >> 7) );
3995 *cur++ = scale * ((*in >> 6) & 0x01);
3996 *cur++ = scale * ((*in >> 5) & 0x01);
3997 *cur++ = scale * ((*in >> 4) & 0x01);
3998 *cur++ = scale * ((*in >> 3) & 0x01);
3999 *cur++ = scale * ((*in >> 2) & 0x01);
4000 *cur++ = scale * ((*in >> 1) & 0x01);
4001 *cur++ = scale * ((*in ) & 0x01);
4002 }
4003 if (k > 0) *cur++ = scale * ((*in >> 7) );
4004 if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
4005 if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
4006 if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
4007 if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
4008 if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
4009 if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
4010 }
4011 if (img_n != out_n) {
4012 // insert alpha = 255
4013 stbi_uc *cur = a->out + stride*j;
4014 int i;
4015 if (img_n == 1) {
4016 for (i=x-1; i >= 0; --i) {
4017 cur[i*2+1] = 255;
4018 cur[i*2+0] = cur[i];
4019 }
4020 } else {
4021 STBI_ASSERT(img_n == 3);
4022 for (i=x-1; i >= 0; --i) {
4023 cur[i*4+3] = 255;
4024 cur[i*4+2] = cur[i*3+2];
4025 cur[i*4+1] = cur[i*3+1];
4026 cur[i*4+0] = cur[i*3+0];
4027 }
4028 }
4029 }
4030 }
4031 }
4032
4033 return 1;
4034}
4035
4036static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
4037{
4038 stbi_uc *final;
4039 int p;
4040 if (!interlaced)
4041 return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
4042
4043 // de-interlacing
4044 final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n);
4045 for (p=0; p < 7; ++p) {
4046 int xorig[] = { 0,4,0,2,0,1,0 };
4047 int yorig[] = { 0,0,4,0,2,0,1 };
4048 int xspc[] = { 8,8,4,4,2,2,1 };
4049 int yspc[] = { 8,8,8,4,4,2,2 };
4050 int i,j,x,y;
4051 // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
4052 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
4053 y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
4054 if (x && y) {
4055 stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
4056 if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
4057 STBI_FREE(final);
4058 return 0;
4059 }
4060 for (j=0; j < y; ++j) {
4061 for (i=0; i < x; ++i) {
4062 int out_y = j*yspc[p]+yorig[p];
4063 int out_x = i*xspc[p]+xorig[p];
4064 memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n,
4065 a->out + (j*x+i)*out_n, out_n);
4066 }
4067 }
4068 STBI_FREE(a->out);
4069 image_data += img_len;
4070 image_data_len -= img_len;
4071 }
4072 }
4073 a->out = final;
4074
4075 return 1;
4076}
4077
4078static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
4079{
4080 stbi__context *s = z->s;
4081 stbi__uint32 i, pixel_count = s->img_x * s->img_y;
4082 stbi_uc *p = z->out;
4083
4084 // compute color-based transparency, assuming we've
4085 // already got 255 as the alpha value in the output
4086 STBI_ASSERT(out_n == 2 || out_n == 4);
4087
4088 if (out_n == 2) {
4089 for (i=0; i < pixel_count; ++i) {
4090 p[1] = (p[0] == tc[0] ? 0 : 255);
4091 p += 2;
4092 }
4093 } else {
4094 for (i=0; i < pixel_count; ++i) {
4095 if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
4096 p[3] = 0;
4097 p += 4;
4098 }
4099 }
4100 return 1;
4101}
4102
4103static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
4104{
4105 stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
4106 stbi_uc *p, *temp_out, *orig = a->out;
4107
4108 p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n);
4109 if (p == NULL) return stbi__err("outofmem", "Out of memory");
4110
4111 // between here and free(out) below, exitting would leak
4112 temp_out = p;
4113
4114 if (pal_img_n == 3) {
4115 for (i=0; i < pixel_count; ++i) {
4116 int n = orig[i]*4;
4117 p[0] = palette[n ];
4118 p[1] = palette[n+1];
4119 p[2] = palette[n+2];
4120 p += 3;
4121 }
4122 } else {
4123 for (i=0; i < pixel_count; ++i) {
4124 int n = orig[i]*4;
4125 p[0] = palette[n ];
4126 p[1] = palette[n+1];
4127 p[2] = palette[n+2];
4128 p[3] = palette[n+3];
4129 p += 4;
4130 }
4131 }
4132 STBI_FREE(a->out);
4133 a->out = temp_out;
4134
4135 STBI_NOTUSED(len);
4136
4137 return 1;
4138}
4139
4140static int stbi__unpremultiply_on_load = 0;
4141static int stbi__de_iphone_flag = 0;
4142
4143STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
4144{
4145 stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
4146}
4147
4148STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
4149{
4150 stbi__de_iphone_flag = flag_true_if_should_convert;
4151}
4152
4153static void stbi__de_iphone(stbi__png *z)
4154{
4155 stbi__context *s = z->s;
4156 stbi__uint32 i, pixel_count = s->img_x * s->img_y;
4157 stbi_uc *p = z->out;
4158
4159 if (s->img_out_n == 3) { // convert bgr to rgb
4160 for (i=0; i < pixel_count; ++i) {
4161 stbi_uc t = p[0];
4162 p[0] = p[2];
4163 p[2] = t;
4164 p += 3;
4165 }
4166 } else {
4167 STBI_ASSERT(s->img_out_n == 4);
4168 if (stbi__unpremultiply_on_load) {
4169 // convert bgr to rgb and unpremultiply
4170 for (i=0; i < pixel_count; ++i) {
4171 stbi_uc a = p[3];
4172 stbi_uc t = p[0];
4173 if (a) {
4174 p[0] = p[2] * 255 / a;
4175 p[1] = p[1] * 255 / a;
4176 p[2] = t * 255 / a;
4177 } else {
4178 p[0] = p[2];
4179 p[2] = t;
4180 }
4181 p += 4;
4182 }
4183 } else {
4184 // convert bgr to rgb
4185 for (i=0; i < pixel_count; ++i) {
4186 stbi_uc t = p[0];
4187 p[0] = p[2];
4188 p[2] = t;
4189 p += 4;
4190 }
4191 }
4192 }
4193}
4194
4195#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
4196
4197static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
4198{
4199 stbi_uc palette[1024], pal_img_n=0;
4200 stbi_uc has_trans=0, tc[3];
4201 stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
4202 int first=1,k,interlace=0, color=0, depth=0, is_iphone=0;
4203 stbi__context *s = z->s;
4204
4205 z->expanded = NULL;
4206 z->idata = NULL;
4207 z->out = NULL;
4208
4209 if (!stbi__check_png_header(s)) return 0;
4210
4211 if (scan == STBI__SCAN_type) return 1;
4212
4213 for (;;) {
4214 stbi__pngchunk c = stbi__get_chunk_header(s);
4215 switch (c.type) {
4216 case STBI__PNG_TYPE('C','g','B','I'):
4217 is_iphone = 1;
4218 stbi__skip(s, c.length);
4219 break;
4220 case STBI__PNG_TYPE('I','H','D','R'): {
4221 int comp,filter;
4222 if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
4223 first = 0;
4224 if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
4225 s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
4226 s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
4227 depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only");
4228 color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
4229 if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
4230 comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
4231 filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
4232 interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
4233 if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
4234 if (!pal_img_n) {
4235 s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
4236 if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
4237 if (scan == STBI__SCAN_header) return 1;
4238 } else {
4239 // if paletted, then pal_n is our final components, and
4240 // img_n is # components to decompress/filter.
4241 s->img_n = 1;
4242 if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
4243 // if SCAN_header, have to scan to see if we have a tRNS
4244 }
4245 break;
4246 }
4247
4248 case STBI__PNG_TYPE('P','L','T','E'): {
4249 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4250 if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
4251 pal_len = c.length / 3;
4252 if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
4253 for (i=0; i < pal_len; ++i) {
4254 palette[i*4+0] = stbi__get8(s);
4255 palette[i*4+1] = stbi__get8(s);
4256 palette[i*4+2] = stbi__get8(s);
4257 palette[i*4+3] = 255;
4258 }
4259 break;
4260 }
4261
4262 case STBI__PNG_TYPE('t','R','N','S'): {
4263 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4264 if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
4265 if (pal_img_n) {
4266 if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
4267 if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
4268 if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
4269 pal_img_n = 4;
4270 for (i=0; i < c.length; ++i)
4271 palette[i*4+3] = stbi__get8(s);
4272 } else {
4273 if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
4274 if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
4275 has_trans = 1;
4276 for (k=0; k < s->img_n; ++k)
4277 tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger
4278 }
4279 break;
4280 }
4281
4282 case STBI__PNG_TYPE('I','D','A','T'): {
4283 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4284 if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
4285 if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
4286 if (ioff + c.length > idata_limit) {
4287 stbi_uc *p;
4288 if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
4289 while (ioff + c.length > idata_limit)
4290 idata_limit *= 2;
4291 p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
4292 z->idata = p;
4293 }
4294 if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
4295 ioff += c.length;
4296 break;
4297 }
4298
4299 case STBI__PNG_TYPE('I','E','N','D'): {
4300 stbi__uint32 raw_len, bpl;
4301 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4302 if (scan != STBI__SCAN_load) return 1;
4303 if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
4304 // initial guess for decoded data size to avoid unnecessary reallocs
4305 bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component
4306 raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
4307 z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
4308 if (z->expanded == NULL) return 0; // zlib should set error
4309 STBI_FREE(z->idata); z->idata = NULL;
4310 if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
4311 s->img_out_n = s->img_n+1;
4312 else
4313 s->img_out_n = s->img_n;
4314 if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0;
4315 if (has_trans)
4316 if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
4317 if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
4318 stbi__de_iphone(z);
4319 if (pal_img_n) {
4320 // pal_img_n == 3 or 4
4321 s->img_n = pal_img_n; // record the actual colors we had
4322 s->img_out_n = pal_img_n;
4323 if (req_comp >= 3) s->img_out_n = req_comp;
4324 if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
4325 return 0;
4326 }
4327 STBI_FREE(z->expanded); z->expanded = NULL;
4328 return 1;
4329 }
4330
4331 default:
4332 // if critical, fail
4333 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4334 if ((c.type & (1 << 29)) == 0) {
4335 #ifndef STBI_NO_FAILURE_STRINGS
4336 // not threadsafe
4337 static char invalid_chunk[] = "XXXX PNG chunk not known";
4338 invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
4339 invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
4340 invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
4341 invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
4342 #endif
4343 return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
4344 }
4345 stbi__skip(s, c.length);
4346 break;
4347 }
4348 // end of PNG chunk, read and skip CRC
4349 stbi__get32be(s);
4350 }
4351}
4352
4353static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp)
4354{
4355 unsigned char *result=NULL;
4356 if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
4357 if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
4358 result = p->out;
4359 p->out = NULL;
4360 if (req_comp && req_comp != p->s->img_out_n) {
4361 result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
4362 p->s->img_out_n = req_comp;
4363 if (result == NULL) return result;
4364 }
4365 *x = p->s->img_x;
4366 *y = p->s->img_y;
4367 if (n) *n = p->s->img_out_n;
4368 }
4369 STBI_FREE(p->out); p->out = NULL;
4370 STBI_FREE(p->expanded); p->expanded = NULL;
4371 STBI_FREE(p->idata); p->idata = NULL;
4372
4373 return result;
4374}
4375
4376static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
4377{
4378 stbi__png p;
4379 p.s = s;
4380 return stbi__do_png(&p, x,y,comp,req_comp);
4381}
4382
4383static int stbi__png_test(stbi__context *s)
4384{
4385 int r;
4386 r = stbi__check_png_header(s);
4387 stbi__rewind(s);
4388 return r;
4389}
4390
4391static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
4392{
4393 if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
4394 stbi__rewind( p->s );
4395 return 0;
4396 }
4397 if (x) *x = p->s->img_x;
4398 if (y) *y = p->s->img_y;
4399 if (comp) *comp = p->s->img_n;
4400 return 1;
4401}
4402
4403static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
4404{
4405 stbi__png p;
4406 p.s = s;
4407 return stbi__png_info_raw(&p, x, y, comp);
4408}
4409#endif
4410
4411// Microsoft/Windows BMP image
4412
4413#ifndef STBI_NO_BMP
4414static int stbi__bmp_test_raw(stbi__context *s)
4415{
4416 int r;
4417 int sz;
4418 if (stbi__get8(s) != 'B') return 0;
4419 if (stbi__get8(s) != 'M') return 0;
4420 stbi__get32le(s); // discard filesize
4421 stbi__get16le(s); // discard reserved
4422 stbi__get16le(s); // discard reserved
4423 stbi__get32le(s); // discard data offset
4424 sz = stbi__get32le(s);
4425 r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
4426 return r;
4427}
4428
4429static int stbi__bmp_test(stbi__context *s)
4430{
4431 int r = stbi__bmp_test_raw(s);
4432 stbi__rewind(s);
4433 return r;
4434}
4435
4436
4437// returns 0..31 for the highest set bit
4438static int stbi__high_bit(unsigned int z)
4439{
4440 int n=0;
4441 if (z == 0) return -1;
4442 if (z >= 0x10000) n += 16, z >>= 16;
4443 if (z >= 0x00100) n += 8, z >>= 8;
4444 if (z >= 0x00010) n += 4, z >>= 4;
4445 if (z >= 0x00004) n += 2, z >>= 2;
4446 if (z >= 0x00002) n += 1, z >>= 1;
4447 return n;
4448}
4449
4450static int stbi__bitcount(unsigned int a)
4451{
4452 a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
4453 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
4454 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
4455 a = (a + (a >> 8)); // max 16 per 8 bits
4456 a = (a + (a >> 16)); // max 32 per 8 bits
4457 return a & 0xff;
4458}
4459
4460static int stbi__shiftsigned(int v, int shift, int bits)
4461{
4462 int result;
4463 int z=0;
4464
4465 if (shift < 0) v <<= -shift;
4466 else v >>= shift;
4467 result = v;
4468
4469 z = bits;
4470 while (z < 8) {
4471 result += v >> z;
4472 z += bits;
4473 }
4474 return result;
4475}
4476
4477static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
4478{
4479 stbi_uc *out;
4480 unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
4481 stbi_uc pal[256][4];
4482 int psize=0,i,j,compress=0,width;
4483 int bpp, flip_vertically, pad, target, offset, hsz;
4484 if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
4485 stbi__get32le(s); // discard filesize
4486 stbi__get16le(s); // discard reserved
4487 stbi__get16le(s); // discard reserved
4488 offset = stbi__get32le(s);
4489 hsz = stbi__get32le(s);
4490 if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
4491 if (hsz == 12) {
4492 s->img_x = stbi__get16le(s);
4493 s->img_y = stbi__get16le(s);
4494 } else {
4495 s->img_x = stbi__get32le(s);
4496 s->img_y = stbi__get32le(s);
4497 }
4498 if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
4499 bpp = stbi__get16le(s);
4500 if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit");
4501 flip_vertically = ((int) s->img_y) > 0;
4502 s->img_y = abs((int) s->img_y);
4503 if (hsz == 12) {
4504 if (bpp < 24)
4505 psize = (offset - 14 - 24) / 3;
4506 } else {
4507 compress = stbi__get32le(s);
4508 if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
4509 stbi__get32le(s); // discard sizeof
4510 stbi__get32le(s); // discard hres
4511 stbi__get32le(s); // discard vres
4512 stbi__get32le(s); // discard colorsused
4513 stbi__get32le(s); // discard max important
4514 if (hsz == 40 || hsz == 56) {
4515 if (hsz == 56) {
4516 stbi__get32le(s);
4517 stbi__get32le(s);
4518 stbi__get32le(s);
4519 stbi__get32le(s);
4520 }
4521 if (bpp == 16 || bpp == 32) {
4522 mr = mg = mb = 0;
4523 if (compress == 0) {
4524 if (bpp == 32) {
4525 mr = 0xffu << 16;
4526 mg = 0xffu << 8;
4527 mb = 0xffu << 0;
4528 ma = 0xffu << 24;
4529 fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
4530 STBI_NOTUSED(fake_a);
4531 } else {
4532 mr = 31u << 10;
4533 mg = 31u << 5;
4534 mb = 31u << 0;
4535 }
4536 } else if (compress == 3) {
4537 mr = stbi__get32le(s);
4538 mg = stbi__get32le(s);
4539 mb = stbi__get32le(s);
4540 // not documented, but generated by photoshop and handled by mspaint
4541 if (mr == mg && mg == mb) {
4542 // ?!?!?
4543 return stbi__errpuc("bad BMP", "bad BMP");
4544 }
4545 } else
4546 return stbi__errpuc("bad BMP", "bad BMP");
4547 }
4548 } else {
4549 STBI_ASSERT(hsz == 108 || hsz == 124);
4550 mr = stbi__get32le(s);
4551 mg = stbi__get32le(s);
4552 mb = stbi__get32le(s);
4553 ma = stbi__get32le(s);
4554 stbi__get32le(s); // discard color space
4555 for (i=0; i < 12; ++i)
4556 stbi__get32le(s); // discard color space parameters
4557 if (hsz == 124) {
4558 stbi__get32le(s); // discard rendering intent
4559 stbi__get32le(s); // discard offset of profile data
4560 stbi__get32le(s); // discard size of profile data
4561 stbi__get32le(s); // discard reserved
4562 }
4563 }
4564 if (bpp < 16)
4565 psize = (offset - 14 - hsz) >> 2;
4566 }
4567 s->img_n = ma ? 4 : 3;
4568 if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
4569 target = req_comp;
4570 else
4571 target = s->img_n; // if they want monochrome, we'll post-convert
4572 out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y);
4573 if (!out) return stbi__errpuc("outofmem", "Out of memory");
4574 if (bpp < 16) {
4575 int z=0;
4576 if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
4577 for (i=0; i < psize; ++i) {
4578 pal[i][2] = stbi__get8(s);
4579 pal[i][1] = stbi__get8(s);
4580 pal[i][0] = stbi__get8(s);
4581 if (hsz != 12) stbi__get8(s);
4582 pal[i][3] = 255;
4583 }
4584 stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
4585 if (bpp == 4) width = (s->img_x + 1) >> 1;
4586 else if (bpp == 8) width = s->img_x;
4587 else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
4588 pad = (-width)&3;
4589 for (j=0; j < (int) s->img_y; ++j) {
4590 for (i=0; i < (int) s->img_x; i += 2) {
4591 int v=stbi__get8(s),v2=0;
4592 if (bpp == 4) {
4593 v2 = v & 15;
4594 v >>= 4;
4595 }
4596 out[z++] = pal[v][0];
4597 out[z++] = pal[v][1];
4598 out[z++] = pal[v][2];
4599 if (target == 4) out[z++] = 255;
4600 if (i+1 == (int) s->img_x) break;
4601 v = (bpp == 8) ? stbi__get8(s) : v2;
4602 out[z++] = pal[v][0];
4603 out[z++] = pal[v][1];
4604 out[z++] = pal[v][2];
4605 if (target == 4) out[z++] = 255;
4606 }
4607 stbi__skip(s, pad);
4608 }
4609 } else {
4610 int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
4611 int z = 0;
4612 int easy=0;
4613 stbi__skip(s, offset - 14 - hsz);
4614 if (bpp == 24) width = 3 * s->img_x;
4615 else if (bpp == 16) width = 2*s->img_x;
4616 else /* bpp = 32 and pad = 0 */ width=0;
4617 pad = (-width) & 3;
4618 if (bpp == 24) {
4619 easy = 1;
4620 } else if (bpp == 32) {
4621 if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
4622 easy = 2;
4623 }
4624 if (!easy) {
4625 if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
4626 // right shift amt to put high bit in position #7
4627 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr);
4628 gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg);
4629 bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb);
4630 ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma);
4631 }
4632 for (j=0; j < (int) s->img_y; ++j) {
4633 if (easy) {
4634 for (i=0; i < (int) s->img_x; ++i) {
4635 unsigned char a;
4636 out[z+2] = stbi__get8(s);
4637 out[z+1] = stbi__get8(s);
4638 out[z+0] = stbi__get8(s);
4639 z += 3;
4640 a = (easy == 2 ? stbi__get8(s) : 255);
4641 if (target == 4) out[z++] = a;
4642 }
4643 } else {
4644 for (i=0; i < (int) s->img_x; ++i) {
4645 stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s));
4646 int a;
4647 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
4648 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
4649 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
4650 a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
4651 if (target == 4) out[z++] = STBI__BYTECAST(a);
4652 }
4653 }
4654 stbi__skip(s, pad);
4655 }
4656 }
4657 if (flip_vertically) {
4658 stbi_uc t;
4659 for (j=0; j < (int) s->img_y>>1; ++j) {
4660 stbi_uc *p1 = out + j *s->img_x*target;
4661 stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
4662 for (i=0; i < (int) s->img_x*target; ++i) {
4663 t = p1[i], p1[i] = p2[i], p2[i] = t;
4664 }
4665 }
4666 }
4667
4668 if (req_comp && req_comp != target) {
4669 out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
4670 if (out == NULL) return out; // stbi__convert_format frees input on failure
4671 }
4672
4673 *x = s->img_x;
4674 *y = s->img_y;
4675 if (comp) *comp = s->img_n;
4676 return out;
4677}
4678#endif
4679
4680// Targa Truevision - TGA
4681// by Jonathan Dummer
4682#ifndef STBI_NO_TGA
4683static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
4684{
4685 int tga_w, tga_h, tga_comp;
4686 int sz;
4687 stbi__get8(s); // discard Offset
4688 sz = stbi__get8(s); // color type
4689 if( sz > 1 ) {
4690 stbi__rewind(s);
4691 return 0; // only RGB or indexed allowed
4692 }
4693 sz = stbi__get8(s); // image type
4694 // only RGB or grey allowed, +/- RLE
4695 if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0;
4696 stbi__skip(s,9);
4697 tga_w = stbi__get16le(s);
4698 if( tga_w < 1 ) {
4699 stbi__rewind(s);
4700 return 0; // test width
4701 }
4702 tga_h = stbi__get16le(s);
4703 if( tga_h < 1 ) {
4704 stbi__rewind(s);
4705 return 0; // test height
4706 }
4707 sz = stbi__get8(s); // bits per pixel
4708 // only RGB or RGBA or grey allowed
4709 if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) {
4710 stbi__rewind(s);
4711 return 0;
4712 }
4713 tga_comp = sz;
4714 if (x) *x = tga_w;
4715 if (y) *y = tga_h;
4716 if (comp) *comp = tga_comp / 8;
4717 return 1; // seems to have passed everything
4718}
4719
4720static int stbi__tga_test(stbi__context *s)
4721{
4722 int res;
4723 int sz;
4724 stbi__get8(s); // discard Offset
4725 sz = stbi__get8(s); // color type
4726 if ( sz > 1 ) return 0; // only RGB or indexed allowed
4727 sz = stbi__get8(s); // image type
4728 if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE
4729 stbi__get16be(s); // discard palette start
4730 stbi__get16be(s); // discard palette length
4731 stbi__get8(s); // discard bits per palette color entry
4732 stbi__get16be(s); // discard x origin
4733 stbi__get16be(s); // discard y origin
4734 if ( stbi__get16be(s) < 1 ) return 0; // test width
4735 if ( stbi__get16be(s) < 1 ) return 0; // test height
4736 sz = stbi__get8(s); // bits per pixel
4737 if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) )
4738 res = 0;
4739 else
4740 res = 1;
4741 stbi__rewind(s);
4742 return res;
4743}
4744
4745static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
4746{
4747 // read in the TGA header stuff
4748 int tga_offset = stbi__get8(s);
4749 int tga_indexed = stbi__get8(s);
4750 int tga_image_type = stbi__get8(s);
4751 int tga_is_RLE = 0;
4752 int tga_palette_start = stbi__get16le(s);
4753 int tga_palette_len = stbi__get16le(s);
4754 int tga_palette_bits = stbi__get8(s);
4755 int tga_x_origin = stbi__get16le(s);
4756 int tga_y_origin = stbi__get16le(s);
4757 int tga_width = stbi__get16le(s);
4758 int tga_height = stbi__get16le(s);
4759 int tga_bits_per_pixel = stbi__get8(s);
4760 int tga_comp = tga_bits_per_pixel / 8;
4761 int tga_inverted = stbi__get8(s);
4762 // image data
4763 unsigned char *tga_data;
4764 unsigned char *tga_palette = NULL;
4765 int i, j;
4766 unsigned char raw_data[4];
4767 int RLE_count = 0;
4768 int RLE_repeating = 0;
4769 int read_next_pixel = 1;
4770
4771 // do a tiny bit of precessing
4772 if ( tga_image_type >= 8 )
4773 {
4774 tga_image_type -= 8;
4775 tga_is_RLE = 1;
4776 }
4777 /* int tga_alpha_bits = tga_inverted & 15; */
4778 tga_inverted = 1 - ((tga_inverted >> 5) & 1);
4779
4780 // error check
4781 if ( //(tga_indexed) ||
4782 (tga_width < 1) || (tga_height < 1) ||
4783 (tga_image_type < 1) || (tga_image_type > 3) ||
4784 ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) &&
4785 (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32))
4786 )
4787 {
4788 return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA
4789 }
4790
4791 // If I'm paletted, then I'll use the number of bits from the palette
4792 if ( tga_indexed )
4793 {
4794 tga_comp = tga_palette_bits / 8;
4795 }
4796
4797 // tga info
4798 *x = tga_width;
4799 *y = tga_height;
4800 if (comp) *comp = tga_comp;
4801
4802 tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp );
4803 if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
4804
4805 // skip to the data's starting position (offset usually = 0)
4806 stbi__skip(s, tga_offset );
4807
4808 if ( !tga_indexed && !tga_is_RLE) {
4809 for (i=0; i < tga_height; ++i) {
4810 int y = tga_inverted ? tga_height -i - 1 : i;
4811 stbi_uc *tga_row = tga_data + y*tga_width*tga_comp;
4812 stbi__getn(s, tga_row, tga_width * tga_comp);
4813 }
4814 } else {
4815 // do I need to load a palette?
4816 if ( tga_indexed)
4817 {
4818 // any data to skip? (offset usually = 0)
4819 stbi__skip(s, tga_palette_start );
4820 // load the palette
4821 tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 );
4822 if (!tga_palette) {
4823 STBI_FREE(tga_data);
4824 return stbi__errpuc("outofmem", "Out of memory");
4825 }
4826 if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) {
4827 STBI_FREE(tga_data);
4828 STBI_FREE(tga_palette);
4829 return stbi__errpuc("bad palette", "Corrupt TGA");
4830 }
4831 }
4832 // load the data
4833 for (i=0; i < tga_width * tga_height; ++i)
4834 {
4835 // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
4836 if ( tga_is_RLE )
4837 {
4838 if ( RLE_count == 0 )
4839 {
4840 // yep, get the next byte as a RLE command
4841 int RLE_cmd = stbi__get8(s);
4842 RLE_count = 1 + (RLE_cmd & 127);
4843 RLE_repeating = RLE_cmd >> 7;
4844 read_next_pixel = 1;
4845 } else if ( !RLE_repeating )
4846 {
4847 read_next_pixel = 1;
4848 }
4849 } else
4850 {
4851 read_next_pixel = 1;
4852 }
4853 // OK, if I need to read a pixel, do it now
4854 if ( read_next_pixel )
4855 {
4856 // load however much data we did have
4857 if ( tga_indexed )
4858 {
4859 // read in 1 byte, then perform the lookup
4860 int pal_idx = stbi__get8(s);
4861 if ( pal_idx >= tga_palette_len )
4862 {
4863 // invalid index
4864 pal_idx = 0;
4865 }
4866 pal_idx *= tga_bits_per_pixel / 8;
4867 for (j = 0; j*8 < tga_bits_per_pixel; ++j)
4868 {
4869 raw_data[j] = tga_palette[pal_idx+j];
4870 }
4871 } else
4872 {
4873 // read in the data raw
4874 for (j = 0; j*8 < tga_bits_per_pixel; ++j)
4875 {
4876 raw_data[j] = stbi__get8(s);
4877 }
4878 }
4879 // clear the reading flag for the next pixel
4880 read_next_pixel = 0;
4881 } // end of reading a pixel
4882
4883 // copy data
4884 for (j = 0; j < tga_comp; ++j)
4885 tga_data[i*tga_comp+j] = raw_data[j];
4886
4887 // in case we're in RLE mode, keep counting down
4888 --RLE_count;
4889 }
4890 // do I need to invert the image?
4891 if ( tga_inverted )
4892 {
4893 for (j = 0; j*2 < tga_height; ++j)
4894 {
4895 int index1 = j * tga_width * tga_comp;
4896 int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
4897 for (i = tga_width * tga_comp; i > 0; --i)
4898 {
4899 unsigned char temp = tga_data[index1];
4900 tga_data[index1] = tga_data[index2];
4901 tga_data[index2] = temp;
4902 ++index1;
4903 ++index2;
4904 }
4905 }
4906 }
4907 // clear my palette, if I had one
4908 if ( tga_palette != NULL )
4909 {
4910 STBI_FREE( tga_palette );
4911 }
4912 }
4913
4914 // swap RGB
4915 if (tga_comp >= 3)
4916 {
4917 unsigned char* tga_pixel = tga_data;
4918 for (i=0; i < tga_width * tga_height; ++i)
4919 {
4920 unsigned char temp = tga_pixel[0];
4921 tga_pixel[0] = tga_pixel[2];
4922 tga_pixel[2] = temp;
4923 tga_pixel += tga_comp;
4924 }
4925 }
4926
4927 // convert to target component count
4928 if (req_comp && req_comp != tga_comp)
4929 tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
4930
4931 // the things I do to get rid of an error message, and yet keep
4932 // Microsoft's C compilers happy... [8^(
4933 tga_palette_start = tga_palette_len = tga_palette_bits =
4934 tga_x_origin = tga_y_origin = 0;
4935 // OK, done
4936 return tga_data;
4937}
4938#endif
4939
4940// *************************************************************************************************
4941// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
4942
4943#ifndef STBI_NO_PSD
4944static int stbi__psd_test(stbi__context *s)
4945{
4946 int r = (stbi__get32be(s) == 0x38425053);
4947 stbi__rewind(s);
4948 return r;
4949}
4950
4951static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
4952{
4953 int pixelCount;
4954 int channelCount, compression;
4955 int channel, i, count, len;
4956 int w,h;
4957 stbi_uc *out;
4958
4959 // Check identifier
4960 if (stbi__get32be(s) != 0x38425053) // "8BPS"
4961 return stbi__errpuc("not PSD", "Corrupt PSD image");
4962
4963 // Check file type version.
4964 if (stbi__get16be(s) != 1)
4965 return stbi__errpuc("wrong version", "Unsupported version of PSD image");
4966
4967 // Skip 6 reserved bytes.
4968 stbi__skip(s, 6 );
4969
4970 // Read the number of channels (R, G, B, A, etc).
4971 channelCount = stbi__get16be(s);
4972 if (channelCount < 0 || channelCount > 16)
4973 return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
4974
4975 // Read the rows and columns of the image.
4976 h = stbi__get32be(s);
4977 w = stbi__get32be(s);
4978
4979 // Make sure the depth is 8 bits.
4980 if (stbi__get16be(s) != 8)
4981 return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit");
4982
4983 // Make sure the color mode is RGB.
4984 // Valid options are:
4985 // 0: Bitmap
4986 // 1: Grayscale
4987 // 2: Indexed color
4988 // 3: RGB color
4989 // 4: CMYK color
4990 // 7: Multichannel
4991 // 8: Duotone
4992 // 9: Lab color
4993 if (stbi__get16be(s) != 3)
4994 return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
4995
4996 // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
4997 stbi__skip(s,stbi__get32be(s) );
4998
4999 // Skip the image resources. (resolution, pen tool paths, etc)
5000 stbi__skip(s, stbi__get32be(s) );
5001
5002 // Skip the reserved data.
5003 stbi__skip(s, stbi__get32be(s) );
5004
5005 // Find out if the data is compressed.
5006 // Known values:
5007 // 0: no compression
5008 // 1: RLE compressed
5009 compression = stbi__get16be(s);
5010 if (compression > 1)
5011 return stbi__errpuc("bad compression", "PSD has an unknown compression format");
5012
5013 // Create the destination image.
5014 out = (stbi_uc *) stbi__malloc(4 * w*h);
5015 if (!out) return stbi__errpuc("outofmem", "Out of memory");
5016 pixelCount = w*h;
5017
5018 // Initialize the data to zero.
5019 //memset( out, 0, pixelCount * 4 );
5020
5021 // Finally, the image data.
5022 if (compression) {
5023 // RLE as used by .PSD and .TIFF
5024 // Loop until you get the number of unpacked bytes you are expecting:
5025 // Read the next source byte into n.
5026 // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
5027 // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
5028 // Else if n is 128, noop.
5029 // Endloop
5030
5031 // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
5032 // which we're going to just skip.
5033 stbi__skip(s, h * channelCount * 2 );
5034
5035 // Read the RLE data by channel.
5036 for (channel = 0; channel < 4; channel++) {
5037 stbi_uc *p;
5038
5039 p = out+channel;
5040 if (channel >= channelCount) {
5041 // Fill this channel with default data.
5042 for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
5043 } else {
5044 // Read the RLE data.
5045 count = 0;
5046 while (count < pixelCount) {
5047 len = stbi__get8(s);
5048 if (len == 128) {
5049 // No-op.
5050 } else if (len < 128) {
5051 // Copy next len+1 bytes literally.
5052 len++;
5053 count += len;
5054 while (len) {
5055 *p = stbi__get8(s);
5056 p += 4;
5057 len--;
5058 }
5059 } else if (len > 128) {
5060 stbi_uc val;
5061 // Next -len+1 bytes in the dest are replicated from next source byte.
5062 // (Interpret len as a negative 8-bit int.)
5063 len ^= 0x0FF;
5064 len += 2;
5065 val = stbi__get8(s);
5066 count += len;
5067 while (len) {
5068 *p = val;
5069 p += 4;
5070 len--;
5071 }
5072 }
5073 }
5074 }
5075 }
5076
5077 } else {
5078 // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
5079 // where each channel consists of an 8-bit value for each pixel in the image.
5080
5081 // Read the data by channel.
5082 for (channel = 0; channel < 4; channel++) {
5083 stbi_uc *p;
5084
5085 p = out + channel;
5086 if (channel > channelCount) {
5087 // Fill this channel with default data.
5088 for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
5089 } else {
5090 // Read the data.
5091 for (i = 0; i < pixelCount; i++)
5092 *p = stbi__get8(s), p += 4;
5093 }
5094 }
5095 }
5096
5097 if (req_comp && req_comp != 4) {
5098 out = stbi__convert_format(out, 4, req_comp, w, h);
5099 if (out == NULL) return out; // stbi__convert_format frees input on failure
5100 }
5101
5102 if (comp) *comp = channelCount;
5103 *y = h;
5104 *x = w;
5105
5106 return out;
5107}
5108#endif
5109
5110// *************************************************************************************************
5111// Softimage PIC loader
5112// by Tom Seddon
5113//
5114// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
5115// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
5116
5117#ifndef STBI_NO_PIC
5118static int stbi__pic_is4(stbi__context *s,const char *str)
5119{
5120 int i;
5121 for (i=0; i<4; ++i)
5122 if (stbi__get8(s) != (stbi_uc)str[i])
5123 return 0;
5124
5125 return 1;
5126}
5127
5128static int stbi__pic_test_core(stbi__context *s)
5129{
5130 int i;
5131
5132 if (!stbi__pic_is4(s,"\x53\x80\xF6\x34"))
5133 return 0;
5134
5135 for(i=0;i<84;++i)
5136 stbi__get8(s);
5137
5138 if (!stbi__pic_is4(s,"PICT"))
5139 return 0;
5140
5141 return 1;
5142}
5143
5144typedef struct
5145{
5146 stbi_uc size,type,channel;
5147} stbi__pic_packet;
5148
5149static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest)
5150{
5151 int mask=0x80, i;
5152
5153 for (i=0; i<4; ++i, mask>>=1) {
5154 if (channel & mask) {
5155 if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short");
5156 dest[i]=stbi__get8(s);
5157 }
5158 }
5159
5160 return dest;
5161}
5162
5163static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src)
5164{
5165 int mask=0x80,i;
5166
5167 for (i=0;i<4; ++i, mask>>=1)
5168 if (channel&mask)
5169 dest[i]=src[i];
5170}
5171
5172static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result)
5173{
5174 int act_comp=0,num_packets=0,y,chained;
5175 stbi__pic_packet packets[10];
5176
5177 // this will (should...) cater for even some bizarre stuff like having data
5178 // for the same channel in multiple packets.
5179 do {
5180 stbi__pic_packet *packet;
5181
5182 if (num_packets==sizeof(packets)/sizeof(packets[0]))
5183 return stbi__errpuc("bad format","too many packets");
5184
5185 packet = &packets[num_packets++];
5186
5187 chained = stbi__get8(s);
5188 packet->size = stbi__get8(s);
5189 packet->type = stbi__get8(s);
5190 packet->channel = stbi__get8(s);
5191
5192 act_comp |= packet->channel;
5193
5194 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)");
5195 if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp");
5196 } while (chained);
5197
5198 *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
5199
5200 for(y=0; y<height; ++y) {
5201 int packet_idx;
5202
5203 for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {
5204 stbi__pic_packet *packet = &packets[packet_idx];
5205 stbi_uc *dest = result+y*width*4;
5206
5207 switch (packet->type) {
5208 default:
5209 return stbi__errpuc("bad format","packet has bad compression type");
5210
5211 case 0: {//uncompressed
5212 int x;
5213
5214 for(x=0;x<width;++x, dest+=4)
5215 if (!stbi__readval(s,packet->channel,dest))
5216 return 0;
5217 break;
5218 }
5219
5220 case 1://Pure RLE
5221 {
5222 int left=width, i;
5223
5224 while (left>0) {
5225 stbi_uc count,value[4];
5226
5227 count=stbi__get8(s);
5228 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)");
5229
5230 if (count > left)
5231 count = (stbi_uc) left;
5232
5233 if (!stbi__readval(s,packet->channel,value)) return 0;
5234
5235 for(i=0; i<count; ++i,dest+=4)
5236 stbi__copyval(packet->channel,dest,value);
5237 left -= count;
5238 }
5239 }
5240 break;
5241
5242 case 2: {//Mixed RLE
5243 int left=width;
5244 while (left>0) {
5245 int count = stbi__get8(s), i;
5246 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");
5247
5248 if (count >= 128) { // Repeated
5249 stbi_uc value[4];
5250 int i;
5251
5252 if (count==128)
5253 count = stbi__get16be(s);
5254 else
5255 count -= 127;
5256 if (count > left)
5257 return stbi__errpuc("bad file","scanline overrun");
5258
5259 if (!stbi__readval(s,packet->channel,value))
5260 return 0;
5261
5262 for(i=0;i<count;++i, dest += 4)
5263 stbi__copyval(packet->channel,dest,value);
5264 } else { // Raw
5265 ++count;
5266 if (count>left) return stbi__errpuc("bad file","scanline overrun");
5267
5268 for(i=0;i<count;++i, dest+=4)
5269 if (!stbi__readval(s,packet->channel,dest))
5270 return 0;
5271 }
5272 left-=count;
5273 }
5274 break;
5275 }
5276 }
5277 }
5278 }
5279
5280 return result;
5281}
5282
5283static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp)
5284{
5285 stbi_uc *result;
5286 int i, x,y;
5287
5288 for (i=0; i<92; ++i)
5289 stbi__get8(s);
5290
5291 x = stbi__get16be(s);
5292 y = stbi__get16be(s);
5293 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");
5294 if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode");
5295
5296 stbi__get32be(s); //skip `ratio'
5297 stbi__get16be(s); //skip `fields'
5298 stbi__get16be(s); //skip `pad'
5299
5300 // intermediate buffer is RGBA
5301 result = (stbi_uc *) stbi__malloc(x*y*4);
5302 memset(result, 0xff, x*y*4);
5303
5304 if (!stbi__pic_load_core(s,x,y,comp, result)) {
5305 STBI_FREE(result);
5306 result=0;
5307 }
5308 *px = x;
5309 *py = y;
5310 if (req_comp == 0) req_comp = *comp;
5311 result=stbi__convert_format(result,4,req_comp,x,y);
5312
5313 return result;
5314}
5315
5316static int stbi__pic_test(stbi__context *s)
5317{
5318 int r = stbi__pic_test_core(s);
5319 stbi__rewind(s);
5320 return r;
5321}
5322#endif
5323
5324// *************************************************************************************************
5325// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
5326
5327#ifndef STBI_NO_GIF
5328typedef struct
5329{
5330 stbi__int16 prefix;
5331 stbi_uc first;
5332 stbi_uc suffix;
5333} stbi__gif_lzw;
5334
5335typedef struct
5336{
5337 int w,h;
5338 stbi_uc *out; // output buffer (always 4 components)
5339 int flags, bgindex, ratio, transparent, eflags;
5340 stbi_uc pal[256][4];
5341 stbi_uc lpal[256][4];
5342 stbi__gif_lzw codes[4096];
5343 stbi_uc *color_table;
5344 int parse, step;
5345 int lflags;
5346 int start_x, start_y;
5347 int max_x, max_y;
5348 int cur_x, cur_y;
5349 int line_size;
5350} stbi__gif;
5351
5352static int stbi__gif_test_raw(stbi__context *s)
5353{
5354 int sz;
5355 if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
5356 sz = stbi__get8(s);
5357 if (sz != '9' && sz != '7') return 0;
5358 if (stbi__get8(s) != 'a') return 0;
5359 return 1;
5360}
5361
5362static int stbi__gif_test(stbi__context *s)
5363{
5364 int r = stbi__gif_test_raw(s);
5365 stbi__rewind(s);
5366 return r;
5367}
5368
5369static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp)
5370{
5371 int i;
5372 for (i=0; i < num_entries; ++i) {
5373 pal[i][2] = stbi__get8(s);
5374 pal[i][1] = stbi__get8(s);
5375 pal[i][0] = stbi__get8(s);
5376 pal[i][3] = transp == i ? 0 : 255;
5377 }
5378}
5379
5380static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)
5381{
5382 stbi_uc version;
5383 if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
5384 return stbi__err("not GIF", "Corrupt GIF");
5385
5386 version = stbi__get8(s);
5387 if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF");
5388 if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF");
5389
5390 stbi__g_failure_reason = "";
5391 g->w = stbi__get16le(s);
5392 g->h = stbi__get16le(s);
5393 g->flags = stbi__get8(s);
5394 g->bgindex = stbi__get8(s);
5395 g->ratio = stbi__get8(s);
5396 g->transparent = -1;
5397
5398 if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
5399
5400 if (is_info) return 1;
5401
5402 if (g->flags & 0x80)
5403 stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
5404
5405 return 1;
5406}
5407
5408static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
5409{
5410 stbi__gif g;
5411 if (!stbi__gif_header(s, &g, comp, 1)) {
5412 stbi__rewind( s );
5413 return 0;
5414 }
5415 if (x) *x = g.w;
5416 if (y) *y = g.h;
5417 return 1;
5418}
5419
5420static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
5421{
5422 stbi_uc *p, *c;
5423
5424 // recurse to decode the prefixes, since the linked-list is backwards,
5425 // and working backwards through an interleaved image would be nasty
5426 if (g->codes[code].prefix >= 0)
5427 stbi__out_gif_code(g, g->codes[code].prefix);
5428
5429 if (g->cur_y >= g->max_y) return;
5430
5431 p = &g->out[g->cur_x + g->cur_y];
5432 c = &g->color_table[g->codes[code].suffix * 4];
5433
5434 if (c[3] >= 128) {
5435 p[0] = c[2];
5436 p[1] = c[1];
5437 p[2] = c[0];
5438 p[3] = c[3];
5439 }
5440 g->cur_x += 4;
5441
5442 if (g->cur_x >= g->max_x) {
5443 g->cur_x = g->start_x;
5444 g->cur_y += g->step;
5445
5446 while (g->cur_y >= g->max_y && g->parse > 0) {
5447 g->step = (1 << g->parse) * g->line_size;
5448 g->cur_y = g->start_y + (g->step >> 1);
5449 --g->parse;
5450 }
5451 }
5452}
5453
5454static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
5455{
5456 stbi_uc lzw_cs;
5457 stbi__int32 len, code;
5458 stbi__uint32 first;
5459 stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
5460 stbi__gif_lzw *p;
5461
5462 lzw_cs = stbi__get8(s);
5463 clear = 1 << lzw_cs;
5464 first = 1;
5465 codesize = lzw_cs + 1;
5466 codemask = (1 << codesize) - 1;
5467 bits = 0;
5468 valid_bits = 0;
5469 for (code = 0; code < clear; code++) {
5470 g->codes[code].prefix = -1;
5471 g->codes[code].first = (stbi_uc) code;
5472 g->codes[code].suffix = (stbi_uc) code;
5473 }
5474
5475 // support no starting clear code
5476 avail = clear+2;
5477 oldcode = -1;
5478
5479 len = 0;
5480 for(;;) {
5481 if (valid_bits < codesize) {
5482 if (len == 0) {
5483 len = stbi__get8(s); // start new block
5484 if (len == 0)
5485 return g->out;
5486 }
5487 --len;
5488 bits |= (stbi__int32) stbi__get8(s) << valid_bits;
5489 valid_bits += 8;
5490 } else {
5491 stbi__int32 code = bits & codemask;
5492 bits >>= codesize;
5493 valid_bits -= codesize;
5494 // @OPTIMIZE: is there some way we can accelerate the non-clear path?
5495 if (code == clear) { // clear code
5496 codesize = lzw_cs + 1;
5497 codemask = (1 << codesize) - 1;
5498 avail = clear + 2;
5499 oldcode = -1;
5500 first = 0;
5501 } else if (code == clear + 1) { // end of stream code
5502 stbi__skip(s, len);
5503 while ((len = stbi__get8(s)) > 0)
5504 stbi__skip(s,len);
5505 return g->out;
5506 } else if (code <= avail) {
5507 if (first) return stbi__errpuc("no clear code", "Corrupt GIF");
5508
5509 if (oldcode >= 0) {
5510 p = &g->codes[avail++];
5511 if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF");
5512 p->prefix = (stbi__int16) oldcode;
5513 p->first = g->codes[oldcode].first;
5514 p->suffix = (code == avail) ? p->first : g->codes[code].first;
5515 } else if (code == avail)
5516 return stbi__errpuc("illegal code in raster", "Corrupt GIF");
5517
5518 stbi__out_gif_code(g, (stbi__uint16) code);
5519
5520 if ((avail & codemask) == 0 && avail <= 0x0FFF) {
5521 codesize++;
5522 codemask = (1 << codesize) - 1;
5523 }
5524
5525 oldcode = code;
5526 } else {
5527 return stbi__errpuc("illegal code in raster", "Corrupt GIF");
5528 }
5529 }
5530 }
5531}
5532
5533static void stbi__fill_gif_background(stbi__gif *g)
5534{
5535 int i;
5536 stbi_uc *c = g->pal[g->bgindex];
5537 // @OPTIMIZE: write a dword at a time
5538 for (i = 0; i < g->w * g->h * 4; i += 4) {
5539 stbi_uc *p = &g->out[i];
5540 p[0] = c[2];
5541 p[1] = c[1];
5542 p[2] = c[0];
5543 p[3] = c[3];
5544 }
5545}
5546
5547// this function is designed to support animated gifs, although stb_image doesn't support it
5548static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp)
5549{
5550 int i;
5551 stbi_uc *old_out = 0;
5552
5553 if (g->out == 0) {
5554 if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
5555 g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
5556 if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
5557 stbi__fill_gif_background(g);
5558 } else {
5559 // animated-gif-only path
5560 if (((g->eflags & 0x1C) >> 2) == 3) {
5561 old_out = g->out;
5562 g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
5563 if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
5564 memcpy(g->out, old_out, g->w*g->h*4);
5565 }
5566 }
5567
5568 for (;;) {
5569 switch (stbi__get8(s)) {
5570 case 0x2C: /* Image Descriptor */
5571 {
5572 stbi__int32 x, y, w, h;
5573 stbi_uc *o;
5574
5575 x = stbi__get16le(s);
5576 y = stbi__get16le(s);
5577 w = stbi__get16le(s);
5578 h = stbi__get16le(s);
5579 if (((x + w) > (g->w)) || ((y + h) > (g->h)))
5580 return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
5581
5582 g->line_size = g->w * 4;
5583 g->start_x = x * 4;
5584 g->start_y = y * g->line_size;
5585 g->max_x = g->start_x + w * 4;
5586 g->max_y = g->start_y + h * g->line_size;
5587 g->cur_x = g->start_x;
5588 g->cur_y = g->start_y;
5589
5590 g->lflags = stbi__get8(s);
5591
5592 if (g->lflags & 0x40) {
5593 g->step = 8 * g->line_size; // first interlaced spacing
5594 g->parse = 3;
5595 } else {
5596 g->step = g->line_size;
5597 g->parse = 0;
5598 }
5599
5600 if (g->lflags & 0x80) {
5601 stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
5602 g->color_table = (stbi_uc *) g->lpal;
5603 } else if (g->flags & 0x80) {
5604 for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent
5605 g->pal[i][3] = 255;
5606 if (g->transparent >= 0 && (g->eflags & 0x01))
5607 g->pal[g->transparent][3] = 0;
5608 g->color_table = (stbi_uc *) g->pal;
5609 } else
5610 return stbi__errpuc("missing color table", "Corrupt GIF");
5611
5612 o = stbi__process_gif_raster(s, g);
5613 if (o == NULL) return NULL;
5614
5615 if (req_comp && req_comp != 4)
5616 o = stbi__convert_format(o, 4, req_comp, g->w, g->h);
5617 return o;
5618 }
5619
5620 case 0x21: // Comment Extension.
5621 {
5622 int len;
5623 if (stbi__get8(s) == 0xF9) { // Graphic Control Extension.
5624 len = stbi__get8(s);
5625 if (len == 4) {
5626 g->eflags = stbi__get8(s);
5627 stbi__get16le(s); // delay
5628 g->transparent = stbi__get8(s);
5629 } else {
5630 stbi__skip(s, len);
5631 break;
5632 }
5633 }
5634 while ((len = stbi__get8(s)) != 0)
5635 stbi__skip(s, len);
5636 break;
5637 }
5638
5639 case 0x3B: // gif stream termination code
5640 return (stbi_uc *) s; // using '1' causes warning on some compilers
5641
5642 default:
5643 return stbi__errpuc("unknown code", "Corrupt GIF");
5644 }
5645 }
5646}
5647
5648static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
5649{
5650 stbi_uc *u = 0;
5651 stbi__gif g;
5652 memset(&g, 0, sizeof(g));
5653
5654 u = stbi__gif_load_next(s, &g, comp, req_comp);
5655 if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
5656 if (u) {
5657 *x = g.w;
5658 *y = g.h;
5659 }
5660
5661 return u;
5662}
5663
5664static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp)
5665{
5666 return stbi__gif_info_raw(s,x,y,comp);
5667}
5668#endif
5669
5670// *************************************************************************************************
5671// Radiance RGBE HDR loader
5672// originally by Nicolas Schulz
5673#ifndef STBI_NO_HDR
5674static int stbi__hdr_test_core(stbi__context *s)
5675{
5676 const char *signature = "#?RADIANCE\n";
5677 int i;
5678 for (i=0; signature[i]; ++i)
5679 if (stbi__get8(s) != signature[i])
5680 return 0;
5681 return 1;
5682}
5683
5684static int stbi__hdr_test(stbi__context* s)
5685{
5686 int r = stbi__hdr_test_core(s);
5687 stbi__rewind(s);
5688 return r;
5689}
5690
5691#define STBI__HDR_BUFLEN 1024
5692static char *stbi__hdr_gettoken(stbi__context *z, char *buffer)
5693{
5694 int len=0;
5695 char c = '\0';
5696
5697 c = (char) stbi__get8(z);
5698
5699 while (!stbi__at_eof(z) && c != '\n') {
5700 buffer[len++] = c;
5701 if (len == STBI__HDR_BUFLEN-1) {
5702 // flush to end of line
5703 while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
5704 ;
5705 break;
5706 }
5707 c = (char) stbi__get8(z);
5708 }
5709
5710 buffer[len] = 0;
5711 return buffer;
5712}
5713
5714static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
5715{
5716 if ( input[3] != 0 ) {
5717 float f1;
5718 // Exponent
5719 f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
5720 if (req_comp <= 2)
5721 output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
5722 else {
5723 output[0] = input[0] * f1;
5724 output[1] = input[1] * f1;
5725 output[2] = input[2] * f1;
5726 }
5727 if (req_comp == 2) output[1] = 1;
5728 if (req_comp == 4) output[3] = 1;
5729 } else {
5730 switch (req_comp) {
5731 case 4: output[3] = 1; /* fallthrough */
5732 case 3: output[0] = output[1] = output[2] = 0;
5733 break;
5734 case 2: output[1] = 1; /* fallthrough */
5735 case 1: output[0] = 0;
5736 break;
5737 }
5738 }
5739}
5740
5741static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
5742{
5743 char buffer[STBI__HDR_BUFLEN];
5744 char *token;
5745 int valid = 0;
5746 int width, height;
5747 stbi_uc *scanline;
5748 float *hdr_data;
5749 int len;
5750 unsigned char count, value;
5751 int i, j, k, c1,c2, z;
5752
5753
5754 // Check identifier
5755 if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0)
5756 return stbi__errpf("not HDR", "Corrupt HDR image");
5757
5758 // Parse header
5759 for(;;) {
5760 token = stbi__hdr_gettoken(s,buffer);
5761 if (token[0] == 0) break;
5762 if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
5763 }
5764
5765 if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format");
5766
5767 // Parse width and height
5768 // can't use sscanf() if we're not using stdio!
5769 token = stbi__hdr_gettoken(s,buffer);
5770 if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
5771 token += 3;
5772 height = (int) strtol(token, &token, 10);
5773 while (*token == ' ') ++token;
5774 if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
5775 token += 3;
5776 width = (int) strtol(token, NULL, 10);
5777
5778 *x = width;
5779 *y = height;
5780
5781 if (comp) *comp = 3;
5782 if (req_comp == 0) req_comp = 3;
5783
5784 // Read data
5785 hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float));
5786
5787 // Load image data
5788 // image data is stored as some number of sca
5789 if ( width < 8 || width >= 32768) {
5790 // Read flat data
5791 for (j=0; j < height; ++j) {
5792 for (i=0; i < width; ++i) {
5793 stbi_uc rgbe[4];
5794 main_decode_loop:
5795 stbi__getn(s, rgbe, 4);
5796 stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
5797 }
5798 }
5799 } else {
5800 // Read RLE-encoded data
5801 scanline = NULL;
5802
5803 for (j = 0; j < height; ++j) {
5804 c1 = stbi__get8(s);
5805 c2 = stbi__get8(s);
5806 len = stbi__get8(s);
5807 if (c1 != 2 || c2 != 2 || (len & 0x80)) {
5808 // not run-length encoded, so we have to actually use THIS data as a decoded
5809 // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
5810 stbi_uc rgbe[4];
5811 rgbe[0] = (stbi_uc) c1;
5812 rgbe[1] = (stbi_uc) c2;
5813 rgbe[2] = (stbi_uc) len;
5814 rgbe[3] = (stbi_uc) stbi__get8(s);
5815 stbi__hdr_convert(hdr_data, rgbe, req_comp);
5816 i = 1;
5817 j = 0;
5818 STBI_FREE(scanline);
5819 goto main_decode_loop; // yes, this makes no sense
5820 }
5821 len <<= 8;
5822 len |= stbi__get8(s);
5823 if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
5824 if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4);
5825
5826 for (k = 0; k < 4; ++k) {
5827 i = 0;
5828 while (i < width) {
5829 count = stbi__get8(s);
5830 if (count > 128) {
5831 // Run
5832 value = stbi__get8(s);
5833 count -= 128;
5834 for (z = 0; z < count; ++z)
5835 scanline[i++ * 4 + k] = value;
5836 } else {
5837 // Dump
5838 for (z = 0; z < count; ++z)
5839 scanline[i++ * 4 + k] = stbi__get8(s);
5840 }
5841 }
5842 }
5843 for (i=0; i < width; ++i)
5844 stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
5845 }
5846 STBI_FREE(scanline);
5847 }
5848
5849 return hdr_data;
5850}
5851
5852static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
5853{
5854 char buffer[STBI__HDR_BUFLEN];
5855 char *token;
5856 int valid = 0;
5857
5858 if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) {
5859 stbi__rewind( s );
5860 return 0;
5861 }
5862
5863 for(;;) {
5864 token = stbi__hdr_gettoken(s,buffer);
5865 if (token[0] == 0) break;
5866 if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
5867 }
5868
5869 if (!valid) {
5870 stbi__rewind( s );
5871 return 0;
5872 }
5873 token = stbi__hdr_gettoken(s,buffer);
5874 if (strncmp(token, "-Y ", 3)) {
5875 stbi__rewind( s );
5876 return 0;
5877 }
5878 token += 3;
5879 *y = (int) strtol(token, &token, 10);
5880 while (*token == ' ') ++token;
5881 if (strncmp(token, "+X ", 3)) {
5882 stbi__rewind( s );
5883 return 0;
5884 }
5885 token += 3;
5886 *x = (int) strtol(token, NULL, 10);
5887 *comp = 3;
5888 return 1;
5889}
5890#endif // STBI_NO_HDR
5891
5892#ifndef STBI_NO_BMP
5893static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
5894{
5895 int hsz;
5896 if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') {
5897 stbi__rewind( s );
5898 return 0;
5899 }
5900 stbi__skip(s,12);
5901 hsz = stbi__get32le(s);
5902 if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) {
5903 stbi__rewind( s );
5904 return 0;
5905 }
5906 if (hsz == 12) {
5907 *x = stbi__get16le(s);
5908 *y = stbi__get16le(s);
5909 } else {
5910 *x = stbi__get32le(s);
5911 *y = stbi__get32le(s);
5912 }
5913 if (stbi__get16le(s) != 1) {
5914 stbi__rewind( s );
5915 return 0;
5916 }
5917 *comp = stbi__get16le(s) / 8;
5918 return 1;
5919}
5920#endif
5921
5922#ifndef STBI_NO_PSD
5923static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)
5924{
5925 int channelCount;
5926 if (stbi__get32be(s) != 0x38425053) {
5927 stbi__rewind( s );
5928 return 0;
5929 }
5930 if (stbi__get16be(s) != 1) {
5931 stbi__rewind( s );
5932 return 0;
5933 }
5934 stbi__skip(s, 6);
5935 channelCount = stbi__get16be(s);
5936 if (channelCount < 0 || channelCount > 16) {
5937 stbi__rewind( s );
5938 return 0;
5939 }
5940 *y = stbi__get32be(s);
5941 *x = stbi__get32be(s);
5942 if (stbi__get16be(s) != 8) {
5943 stbi__rewind( s );
5944 return 0;
5945 }
5946 if (stbi__get16be(s) != 3) {
5947 stbi__rewind( s );
5948 return 0;
5949 }
5950 *comp = 4;
5951 return 1;
5952}
5953#endif
5954
5955#ifndef STBI_NO_PIC
5956static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
5957{
5958 int act_comp=0,num_packets=0,chained;
5959 stbi__pic_packet packets[10];
5960
5961 stbi__skip(s, 92);
5962
5963 *x = stbi__get16be(s);
5964 *y = stbi__get16be(s);
5965 if (stbi__at_eof(s)) return 0;
5966 if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
5967 stbi__rewind( s );
5968 return 0;
5969 }
5970
5971 stbi__skip(s, 8);
5972
5973 do {
5974 stbi__pic_packet *packet;
5975
5976 if (num_packets==sizeof(packets)/sizeof(packets[0]))
5977 return 0;
5978
5979 packet = &packets[num_packets++];
5980 chained = stbi__get8(s);
5981 packet->size = stbi__get8(s);
5982 packet->type = stbi__get8(s);
5983 packet->channel = stbi__get8(s);
5984 act_comp |= packet->channel;
5985
5986 if (stbi__at_eof(s)) {
5987 stbi__rewind( s );
5988 return 0;
5989 }
5990 if (packet->size != 8) {
5991 stbi__rewind( s );
5992 return 0;
5993 }
5994 } while (chained);
5995
5996 *comp = (act_comp & 0x10 ? 4 : 3);
5997
5998 return 1;
5999}
6000#endif
6001
6002// *************************************************************************************************
6003// Portable Gray Map and Portable Pixel Map loader
6004// by Ken Miller
6005//
6006// PGM: http://netpbm.sourceforge.net/doc/pgm.html
6007// PPM: http://netpbm.sourceforge.net/doc/ppm.html
6008//
6009// Known limitations:
6010// Does not support comments in the header section
6011// Does not support ASCII image data (formats P2 and P3)
6012// Does not support 16-bit-per-channel
6013
6014#ifndef STBI_NO_PNM
6015
6016static int stbi__pnm_test(stbi__context *s)
6017{
6018 char p, t;
6019 p = (char) stbi__get8(s);
6020 t = (char) stbi__get8(s);
6021 if (p != 'P' || (t != '5' && t != '6')) {
6022 stbi__rewind( s );
6023 return 0;
6024 }
6025 return 1;
6026}
6027
6028static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
6029{
6030 stbi_uc *out;
6031 if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
6032 return 0;
6033 *x = s->img_x;
6034 *y = s->img_y;
6035 *comp = s->img_n;
6036
6037 out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y);
6038 if (!out) return stbi__errpuc("outofmem", "Out of memory");
6039 stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
6040
6041 if (req_comp && req_comp != s->img_n) {
6042 out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
6043 if (out == NULL) return out; // stbi__convert_format frees input on failure
6044 }
6045 return out;
6046}
6047
6048static int stbi__pnm_isspace(char c)
6049{
6050 return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
6051}
6052
6053static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)
6054{
6055 while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
6056 *c = (char) stbi__get8(s);
6057}
6058
6059static int stbi__pnm_isdigit(char c)
6060{
6061 return c >= '0' && c <= '9';
6062}
6063
6064static int stbi__pnm_getinteger(stbi__context *s, char *c)
6065{
6066 int value = 0;
6067
6068 while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
6069 value = value*10 + (*c - '0');
6070 *c = (char) stbi__get8(s);
6071 }
6072
6073 return value;
6074}
6075
6076static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
6077{
6078 int maxv;
6079 char c, p, t;
6080
6081 stbi__rewind( s );
6082
6083 // Get identifier
6084 p = (char) stbi__get8(s);
6085 t = (char) stbi__get8(s);
6086 if (p != 'P' || (t != '5' && t != '6')) {
6087 stbi__rewind( s );
6088 return 0;
6089 }
6090
6091 *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
6092
6093 c = (char) stbi__get8(s);
6094 stbi__pnm_skip_whitespace(s, &c);
6095
6096 *x = stbi__pnm_getinteger(s, &c); // read width
6097 stbi__pnm_skip_whitespace(s, &c);
6098
6099 *y = stbi__pnm_getinteger(s, &c); // read height
6100 stbi__pnm_skip_whitespace(s, &c);
6101
6102 maxv = stbi__pnm_getinteger(s, &c); // read max value
6103
6104 if (maxv > 255)
6105 return stbi__err("max value > 255", "PPM image not 8-bit");
6106 else
6107 return 1;
6108}
6109#endif
6110
6111static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
6112{
6113 #ifndef STBI_NO_JPEG
6114 if (stbi__jpeg_info(s, x, y, comp)) return 1;
6115 #endif
6116
6117 #ifndef STBI_NO_PNG
6118 if (stbi__png_info(s, x, y, comp)) return 1;
6119 #endif
6120
6121 #ifndef STBI_NO_GIF
6122 if (stbi__gif_info(s, x, y, comp)) return 1;
6123 #endif
6124
6125 #ifndef STBI_NO_BMP
6126 if (stbi__bmp_info(s, x, y, comp)) return 1;
6127 #endif
6128
6129 #ifndef STBI_NO_PSD
6130 if (stbi__psd_info(s, x, y, comp)) return 1;
6131 #endif
6132
6133 #ifndef STBI_NO_PIC
6134 if (stbi__pic_info(s, x, y, comp)) return 1;
6135 #endif
6136
6137 #ifndef STBI_NO_PNM
6138 if (stbi__pnm_info(s, x, y, comp)) return 1;
6139 #endif
6140
6141 #ifndef STBI_NO_HDR
6142 if (stbi__hdr_info(s, x, y, comp)) return 1;
6143 #endif
6144
6145 // test tga last because it's a crappy test!
6146 #ifndef STBI_NO_TGA
6147 if (stbi__tga_info(s, x, y, comp))
6148 return 1;
6149 #endif
6150 return stbi__err("unknown image type", "Image not of any known type, or corrupt");
6151}
6152
6153#ifndef STBI_NO_STDIO
6154STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
6155{
6156 FILE *f = stbi__fopen(filename, "rb");
6157 int result;
6158 if (!f) return stbi__err("can't fopen", "Unable to open file");
6159 result = stbi_info_from_file(f, x, y, comp);
6160 fclose(f);
6161 return result;
6162}
6163
6164STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
6165{
6166 int r;
6167 stbi__context s;
6168 long pos = ftell(f);
6169 stbi__start_file(&s, f);
6170 r = stbi__info_main(&s,x,y,comp);
6171 fseek(f,pos,SEEK_SET);
6172 return r;
6173}
6174#endif // !STBI_NO_STDIO
6175
6176STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
6177{
6178 stbi__context s;
6179 stbi__start_mem(&s,buffer,len);
6180 return stbi__info_main(&s,x,y,comp);
6181}
6182
6183STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
6184{
6185 stbi__context s;
6186 stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
6187 return stbi__info_main(&s,x,y,comp);
6188}
6189
6190#endif // STB_IMAGE_IMPLEMENTATION
6191
6192/*
6193 revision history:
6194 2.02 (2015-01-19) fix incorrect assert, fix warning
6195 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
6196 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
6197 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
6198 progressive JPEG (stb)
6199 PGM/PPM support (Ken Miller)
6200 STBI_MALLOC,STBI_REALLOC,STBI_FREE
6201 GIF bugfix -- seemingly never worked
6202 STBI_NO_*, STBI_ONLY_*
6203 1.48 (2014-12-14) fix incorrectly-named assert()
6204 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
6205 optimize PNG (ryg)
6206 fix bug in interlaced PNG with user-specified channel count (stb)
6207 1.46 (2014-08-26)
6208 fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
6209 1.45 (2014-08-16)
6210 fix MSVC-ARM internal compiler error by wrapping malloc
6211 1.44 (2014-08-07)
6212 various warning fixes from Ronny Chevalier
6213 1.43 (2014-07-15)
6214 fix MSVC-only compiler problem in code changed in 1.42
6215 1.42 (2014-07-09)
6216 don't define _CRT_SECURE_NO_WARNINGS (affects user code)
6217 fixes to stbi__cleanup_jpeg path
6218 added STBI_ASSERT to avoid requiring assert.h
6219 1.41 (2014-06-25)
6220 fix search&replace from 1.36 that messed up comments/error messages
6221 1.40 (2014-06-22)
6222 fix gcc struct-initialization warning
6223 1.39 (2014-06-15)
6224 fix to TGA optimization when req_comp != number of components in TGA;
6225 fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
6226 add support for BMP version 5 (more ignored fields)
6227 1.38 (2014-06-06)
6228 suppress MSVC warnings on integer casts truncating values
6229 fix accidental rename of 'skip' field of I/O
6230 1.37 (2014-06-04)
6231 remove duplicate typedef
6232 1.36 (2014-06-03)
6233 convert to header file single-file library
6234 if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
6235 1.35 (2014-05-27)
6236 various warnings
6237 fix broken STBI_SIMD path
6238 fix bug where stbi_load_from_file no longer left file pointer in correct place
6239 fix broken non-easy path for 32-bit BMP (possibly never used)
6240 TGA optimization by Arseny Kapoulkine
6241 1.34 (unknown)
6242 use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
6243 1.33 (2011-07-14)
6244 make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
6245 1.32 (2011-07-13)
6246 support for "info" function for all supported filetypes (SpartanJ)
6247 1.31 (2011-06-20)
6248 a few more leak fixes, bug in PNG handling (SpartanJ)
6249 1.30 (2011-06-11)
6250 added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
6251 removed deprecated format-specific test/load functions
6252 removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
6253 error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
6254 fix inefficiency in decoding 32-bit BMP (David Woo)
6255 1.29 (2010-08-16)
6256 various warning fixes from Aurelien Pocheville
6257 1.28 (2010-08-01)
6258 fix bug in GIF palette transparency (SpartanJ)
6259 1.27 (2010-08-01)
6260 cast-to-stbi_uc to fix warnings
6261 1.26 (2010-07-24)
6262 fix bug in file buffering for PNG reported by SpartanJ
6263 1.25 (2010-07-17)
6264 refix trans_data warning (Won Chun)
6265 1.24 (2010-07-12)
6266 perf improvements reading from files on platforms with lock-heavy fgetc()
6267 minor perf improvements for jpeg
6268 deprecated type-specific functions so we'll get feedback if they're needed
6269 attempt to fix trans_data warning (Won Chun)
6270 1.23 fixed bug in iPhone support
6271 1.22 (2010-07-10)
6272 removed image *writing* support
6273 stbi_info support from Jetro Lauha
6274 GIF support from Jean-Marc Lienher
6275 iPhone PNG-extensions from James Brown
6276 warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
6277 1.21 fix use of 'stbi_uc' in header (reported by jon blow)
6278 1.20 added support for Softimage PIC, by Tom Seddon
6279 1.19 bug in interlaced PNG corruption check (found by ryg)
6280 1.18 2008-08-02
6281 fix a threading bug (local mutable static)
6282 1.17 support interlaced PNG
6283 1.16 major bugfix - stbi__convert_format converted one too many pixels
6284 1.15 initialize some fields for thread safety
6285 1.14 fix threadsafe conversion bug
6286 header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
6287 1.13 threadsafe
6288 1.12 const qualifiers in the API
6289 1.11 Support installable IDCT, colorspace conversion routines
6290 1.10 Fixes for 64-bit (don't use "unsigned long")
6291 optimized upsampling by Fabian "ryg" Giesen
6292 1.09 Fix format-conversion for PSD code (bad global variables!)
6293 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
6294 1.07 attempt to fix C++ warning/errors again
6295 1.06 attempt to fix C++ warning/errors again
6296 1.05 fix TGA loading to return correct *comp and use good luminance calc
6297 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
6298 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
6299 1.02 support for (subset of) HDR files, float interface for preferred access to them
6300 1.01 fix bug: possible bug in handling right-side up bmps... not sure
6301 fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
6302 1.00 interface to zlib that skips zlib header
6303 0.99 correct handling of alpha in palette
6304 0.98 TGA loader by lonesock; dynamically add loaders (untested)
6305 0.97 jpeg errors on too large a file; also catch another malloc failure
6306 0.96 fix detection of invalid v value - particleman@mollyrocket forum
6307 0.95 during header scan, seek to markers in case of padding
6308 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
6309 0.93 handle jpegtran output; verbose errors
6310 0.92 read 4,8,16,24,32-bit BMP files of several formats
6311 0.91 output 24-bit Windows 3.0 BMP files
6312 0.90 fix a few more warnings; bump version number to approach 1.0
6313 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
6314 0.60 fix compiling as c++
6315 0.59 fix warnings: merge Dave Moore's -Wall fixes
6316 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
6317 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
6318 0.56 fix bug: zlib uncompressed mode len vs. nlen
6319 0.55 fix bug: restart_interval not initialized to 0
6320 0.54 allow NULL for 'int *comp'
6321 0.53 fix bug in png 3->4; speedup png decoding
6322 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
6323 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
6324 on 'test' only check type, not whether we support this variant
6325 0.50 first released version
6326*/ \ No newline at end of file
diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..e630c48 --- /dev/null +++ b/src/system.h
@@ -0,0 +1,55 @@
1#ifndef SYSTEM_H_B61A8CEA
2#define SYSTEM_H_B61A8CEA
3
4#include "entity_manager.h"
5
6class Game;
7class Texture;
8
9class System {
10public:
11
12 using id_type = EntityManager::id_type;
13
14 System(Game& game) : game_(game)
15 {
16 }
17
18 virtual ~System() = default;
19
20 /**
21 * Updates the state of a system.
22 *
23 * @param dt - The amount of time in seconds that have passed since the last
24 * update.
25 */
26 virtual void tick(double)
27 {
28 }
29
30 /**
31 * Renders to a texture.
32 *
33 * @param texture - The surface to render to.
34 */
35 virtual void render(Texture&)
36 {
37 }
38
39 /**
40 * Processes keyboard input.
41 *
42 * @param key - The relevant key.
43 *
44 * @param action - The action performed (press, released, etc).
45 */
46 virtual void input(int, int)
47 {
48 }
49
50protected:
51
52 Game& game_;
53};
54
55#endif /* end of include guard: SYSTEM_H_B61A8CEA */
diff --git a/src/system_manager.h b/src/system_manager.h new file mode 100644 index 0000000..b03c3f2 --- /dev/null +++ b/src/system_manager.h
@@ -0,0 +1,68 @@
1#ifndef SYSTEM_MANAGER_H_544E6056
2#define SYSTEM_MANAGER_H_544E6056
3
4#include <list>
5#include <memory>
6#include <map>
7#include <typeindex>
8#include <stdexcept>
9#include "system.h"
10
11class SystemManager {
12private:
13
14 std::list<std::unique_ptr<System>> loop;
15 std::map<std::type_index, System*> systems;
16
17public:
18
19 template <class T, class... Args>
20 void emplaceSystem(Game& game, Args&&... args)
21 {
22 std::unique_ptr<T> ptr(new T(game, std::forward<Args>(args)...));
23 std::type_index systemType = typeid(T);
24
25 systems[systemType] = ptr.get();
26 loop.push_back(std::move(ptr));
27 }
28
29 template <class T>
30 T& getSystem()
31 {
32 std::type_index systemType = typeid(T);
33
34 if (!systems.count(systemType))
35 {
36 throw std::invalid_argument("Cannot get non-existent system");
37 }
38
39 return *dynamic_cast<T*>(systems[systemType]);
40 }
41
42 void tick(double dt)
43 {
44 for (std::unique_ptr<System>& sys : loop)
45 {
46 sys->tick(dt);
47 }
48 }
49
50 virtual void render(Texture& texture)
51 {
52 for (std::unique_ptr<System>& sys : loop)
53 {
54 sys->render(texture);
55 }
56 }
57
58 virtual void input(int key, int action)
59 {
60 for (std::unique_ptr<System>& sys : loop)
61 {
62 sys->input(key, action);
63 }
64 }
65
66};
67
68#endif /* end of include guard: SYSTEM_MANAGER_H_544E6056 */
diff --git a/src/systems/animating.cpp b/src/systems/animating.cpp new file mode 100644 index 0000000..50a32fc --- /dev/null +++ b/src/systems/animating.cpp
@@ -0,0 +1,103 @@
1#include "animating.h"
2#include "game.h"
3#include "components/animatable.h"
4#include "components/transformable.h"
5
6void AnimatingSystem::tick(double)
7{
8 std::set<id_type> spriteEntities =
9 game_.getEntityManager().getEntitiesWithComponents<AnimatableComponent>();
10
11 for (id_type entity : spriteEntities)
12 {
13 auto& sprite = game_.getEntityManager().
14 getComponent<AnimatableComponent>(entity);
15
16 if (sprite.active)
17 {
18 if (!sprite.frozen)
19 {
20 sprite.countdown++;
21 }
22
23 const Animation& anim = sprite.getAnimation();
24 if (sprite.countdown >= anim.getDelay())
25 {
26 sprite.frame++;
27 sprite.countdown = 0;
28
29 if (sprite.frame >= anim.getFirstFrame() + anim.getNumFrames())
30 {
31 sprite.frame = anim.getFirstFrame();
32 }
33 }
34
35 if (sprite.flickering)
36 {
37 sprite.flickerTimer = (sprite.flickerTimer + 1) % 6;
38 }
39 }
40 }
41}
42
43void AnimatingSystem::render(Texture& texture)
44{
45 std::set<id_type> spriteEntities =
46 game_.getEntityManager().getEntitiesWithComponents<
47 AnimatableComponent,
48 TransformableComponent>();
49
50 for (id_type entity : spriteEntities)
51 {
52 auto& sprite = game_.getEntityManager().
53 getComponent<AnimatableComponent>(entity);
54
55 if (sprite.active)
56 {
57 auto& transform = game_.getEntityManager().
58 getComponent<TransformableComponent>(entity);
59
60 double alpha = 1.0;
61 if (sprite.flickering && (sprite.flickerTimer < 3))
62 {
63 alpha = 0.0;
64 }
65
66 Rectangle dstrect {
67 static_cast<int>(transform.pos.x()),
68 static_cast<int>(transform.pos.y()),
69 transform.size.w(),
70 transform.size.h()};
71
72 const AnimationSet& aset = sprite.animationSet;
73 game_.getRenderer().blit(
74 aset.getTexture(),
75 texture,
76 aset.getFrameRect(sprite.frame),
77 dstrect,
78 alpha);
79 }
80 }
81}
82
83void AnimatingSystem::initPrototype(id_type entity)
84{
85 auto& sprite = game_.getEntityManager().
86 getComponent<AnimatableComponent>(entity);
87
88 startAnimation(entity, sprite.origAnimation);
89
90 sprite.countdown = 0;
91 sprite.flickering = false;
92 sprite.flickerTimer = 0;
93 sprite.frozen = false;
94}
95
96void AnimatingSystem::startAnimation(id_type entity, std::string animation)
97{
98 auto& sprite = game_.getEntityManager().
99 getComponent<AnimatableComponent>(entity);
100
101 sprite.animation = std::move(animation);
102 sprite.frame = sprite.getAnimation().getFirstFrame();
103}
diff --git a/src/systems/animating.h b/src/systems/animating.h new file mode 100644 index 0000000..acc6191 --- /dev/null +++ b/src/systems/animating.h
@@ -0,0 +1,25 @@
1#ifndef ANIMATING_H_5BBF0094
2#define ANIMATING_H_5BBF0094
3
4#include "system.h"
5#include <string>
6#include "renderer/texture.h"
7
8class AnimatingSystem : public System {
9public:
10
11 AnimatingSystem(Game& game) : System(game)
12 {
13 }
14
15 void tick(double dt);
16
17 void render(Texture& texture);
18
19 void initPrototype(id_type entity);
20
21 void startAnimation(id_type entity, std::string animation);
22
23};
24
25#endif /* end of include guard: ANIMATING_H_5BBF0094 */
diff --git a/src/systems/controlling.cpp b/src/systems/controlling.cpp new file mode 100644 index 0000000..e65c09a --- /dev/null +++ b/src/systems/controlling.cpp
@@ -0,0 +1,136 @@
1#include "controlling.h"
2#include "game.h"
3#include "components/controllable.h"
4#include "components/orientable.h"
5#include "systems/orienting.h"
6
7void ControllingSystem::tick(double)
8{
9 while (!actions_.empty())
10 {
11 int key = actions_.front().first;
12 int action = actions_.front().second;
13
14 auto entities = game_.getEntityManager().getEntitiesWithComponents<
15 ControllableComponent,
16 OrientableComponent>();
17
18 for (auto entity : entities)
19 {
20 auto& controllable = game_.getEntityManager().
21 getComponent<ControllableComponent>(entity);
22
23 auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>();
24
25 if (action == GLFW_PRESS)
26 {
27 if (key == controllable.getLeftKey())
28 {
29 controllable.setHoldingLeft(true);
30
31 if (!controllable.isFrozen())
32 {
33 orienting.moveLeft(entity);
34 }
35 } else if (key == controllable.getRightKey())
36 {
37 controllable.setHoldingRight(true);
38
39 if (!controllable.isFrozen())
40 {
41 orienting.moveRight(entity);
42 }
43 } else if (key == controllable.getJumpKey())
44 {
45 if (!controllable.isFrozen())
46 {
47 orienting.jump(entity);
48 }
49 } else if (key == controllable.getDropKey())
50 {
51 if (!controllable.isFrozen())
52 {
53 orienting.drop(entity);
54 }
55 }
56 } else if (action == GLFW_RELEASE)
57 {
58 if (key == controllable.getLeftKey())
59 {
60 controllable.setHoldingLeft(false);
61
62 if (!controllable.isFrozen())
63 {
64 if (controllable.isHoldingRight())
65 {
66 orienting.moveRight(entity);
67 } else {
68 orienting.stopWalking(entity);
69 }
70 }
71 } else if (key == controllable.getRightKey())
72 {
73 controllable.setHoldingRight(false);
74
75 if (!controllable.isFrozen())
76 {
77 if (controllable.isHoldingLeft())
78 {
79 orienting.moveLeft(entity);
80 } else {
81 orienting.stopWalking(entity);
82 }
83 }
84 } else if (key == controllable.getDropKey())
85 {
86 if (!controllable.isFrozen())
87 {
88 orienting.stopDropping(entity);
89 }
90 } else if (key == controllable.getJumpKey())
91 {
92 if (!controllable.isFrozen())
93 {
94 orienting.stopJumping(entity);
95 }
96 }
97 }
98 }
99
100 actions_.pop();
101 }
102}
103
104void ControllingSystem::input(int key, int action)
105{
106 actions_.push(std::make_pair(key, action));
107}
108
109void ControllingSystem::freeze(id_type entity)
110{
111 auto& controllable = game_.getEntityManager().
112 getComponent<ControllableComponent>(entity);
113
114 controllable.setFrozen(true);
115}
116
117void ControllingSystem::unfreeze(id_type entity)
118{
119 auto& controllable = game_.getEntityManager().
120 getComponent<ControllableComponent>(entity);
121
122 if (controllable.isFrozen())
123 {
124 controllable.setFrozen(false);
125
126 auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>();
127
128 if (controllable.isHoldingLeft())
129 {
130 orienting.moveLeft(entity);
131 } else if (controllable.isHoldingRight())
132 {
133 orienting.moveRight(entity);
134 }
135 }
136}
diff --git a/src/systems/controlling.h b/src/systems/controlling.h new file mode 100644 index 0000000..d6f0789 --- /dev/null +++ b/src/systems/controlling.h
@@ -0,0 +1,27 @@
1#ifndef CONTROLLING_H_80B1BB8D
2#define CONTROLLING_H_80B1BB8D
3
4#include "system.h"
5#include <queue>
6
7class ControllingSystem : public System {
8public:
9
10 ControllingSystem(Game& game) : System(game)
11 {
12 }
13
14 void tick(double dt);
15
16 void input(int key, int action);
17
18 void freeze(id_type entity);
19
20 void unfreeze(id_type entity);
21
22private:
23
24 std::queue<std::pair<int,int>> actions_;
25};
26
27#endif /* end of include guard: CONTROLLING_H_80B1BB8D */
diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp new file mode 100644 index 0000000..1275e11 --- /dev/null +++ b/src/systems/mapping.cpp
@@ -0,0 +1,187 @@
1#include "mapping.h"
2#include "components/mappable.h"
3#include "systems/realizing.h"
4#include "game.h"
5#include "consts.h"
6
7template <typename Storage>
8inline void addBoundary(
9 Storage& boundaries,
10 int axis,
11 int lower,
12 int upper,
13 MappableComponent::Boundary::Type type)
14{
15 boundaries.emplace(std::piecewise_construct,
16 std::tie(axis),
17 std::tie(axis, lower, upper, type));
18}
19
20void MappingSystem::render(Texture& texture)
21{
22 id_type map =
23 game_.getSystemManager().getSystem<RealizingSystem>().getActiveMap();
24
25 auto& mappable = game_.getEntityManager().
26 getComponent<MappableComponent>(map);
27
28 for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++)
29 {
30 int x = i % MAP_WIDTH;
31 int y = i / MAP_WIDTH;
32 int tile = mappable.tiles[i];
33
34 if (tile > 0)
35 {
36 Rectangle dst {
37 x * TILE_WIDTH,
38 y * TILE_HEIGHT,
39 TILE_WIDTH,
40 TILE_HEIGHT};
41
42 Rectangle src {
43 (tile % TILESET_COLS) * TILE_WIDTH,
44 (tile / TILESET_COLS) * TILE_HEIGHT,
45 TILE_WIDTH,
46 TILE_HEIGHT};
47
48 game_.getRenderer().blit(
49 mappable.tileset,
50 texture,
51 std::move(src),
52 std::move(dst));
53 }
54 }
55
56 int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (mappable.title.size() / 2);
57
58 for (size_t i = 0; i < mappable.title.size(); i++)
59 {
60 Rectangle src {
61 (mappable.title[i] % FONT_COLS) * TILE_WIDTH,
62 (mappable.title[i] / FONT_COLS) * TILE_HEIGHT,
63 TILE_WIDTH,
64 TILE_HEIGHT};
65
66 Rectangle dst {
67 (startX + static_cast<int>(i)) * TILE_WIDTH,
68 24 * TILE_HEIGHT,
69 TILE_WIDTH,
70 TILE_HEIGHT};
71
72 game_.getRenderer().blit(
73 mappable.font,
74 texture,
75 std::move(src),
76 std::move(dst));
77 }
78}
79
80void MappingSystem::generateBoundaries(id_type mapEntity)
81{
82 auto& mappable = game_.getEntityManager().
83 getComponent<MappableComponent>(mapEntity);
84
85 addBoundary(
86 mappable.leftBoundaries,
87 -WALL_GAP,
88 0,
89 MAP_HEIGHT * TILE_HEIGHT,
90 MappableComponent::Boundary::Type::adjacency);
91
92 addBoundary(
93 mappable.rightBoundaries,
94 GAME_WIDTH + WALL_GAP,
95 0,
96 MAP_HEIGHT * TILE_HEIGHT,
97 MappableComponent::Boundary::Type::adjacency);
98
99 addBoundary(
100 mappable.upBoundaries,
101 -WALL_GAP,
102 0,
103 GAME_WIDTH,
104 MappableComponent::Boundary::Type::adjacency);
105
106 addBoundary(
107 mappable.downBoundaries,
108 MAP_HEIGHT * TILE_HEIGHT + WALL_GAP,
109 0,
110 GAME_WIDTH,
111 MappableComponent::Boundary::Type::adjacency);
112
113 for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++)
114 {
115 size_t x = i % MAP_WIDTH;
116 size_t y = i / MAP_WIDTH;
117 int tile = mappable.tiles[i];
118
119 if ((tile >= 5) && (tile <= 7))
120 {
121 addBoundary(
122 mappable.downBoundaries,
123 y * TILE_HEIGHT,
124 x * TILE_WIDTH,
125 (x + 1) * TILE_WIDTH,
126 MappableComponent::Boundary::Type::platform);
127 } else if ((tile > 0) && (tile < 28))
128 {
129 addBoundary(
130 mappable.rightBoundaries,
131 x * TILE_WIDTH,
132 y * TILE_HEIGHT,
133 (y+1) * TILE_HEIGHT,
134 MappableComponent::Boundary::Type::wall);
135
136 addBoundary(
137 mappable.leftBoundaries,
138 (x+1) * TILE_WIDTH,
139 y * TILE_HEIGHT,
140 (y+1) * TILE_HEIGHT,
141 MappableComponent::Boundary::Type::wall);
142
143 addBoundary(
144 mappable.downBoundaries,
145 y * TILE_HEIGHT,
146 x * TILE_WIDTH,
147 (x+1) * TILE_WIDTH,
148 MappableComponent::Boundary::Type::wall);
149
150 addBoundary(
151 mappable.upBoundaries,
152 (y+1) * TILE_HEIGHT,
153 x * TILE_WIDTH,
154 (x+1) * TILE_WIDTH,
155 MappableComponent::Boundary::Type::wall);
156 } else if (tile == 42)
157 {
158 addBoundary(
159 mappable.rightBoundaries,
160 x * TILE_WIDTH,
161 y * TILE_HEIGHT,
162 (y+1) * TILE_HEIGHT,
163 MappableComponent::Boundary::Type::danger);
164
165 addBoundary(
166 mappable.leftBoundaries,
167 (x+1) * TILE_WIDTH,
168 y * TILE_HEIGHT,
169 (y+1) * TILE_HEIGHT,
170 MappableComponent::Boundary::Type::danger);
171
172 addBoundary(
173 mappable.downBoundaries,
174 y * TILE_HEIGHT + 1,
175 x * TILE_WIDTH,
176 (x+1) * TILE_WIDTH,
177 MappableComponent::Boundary::Type::danger);
178
179 addBoundary(
180 mappable.upBoundaries,
181 (y+1) * TILE_HEIGHT,
182 x * TILE_WIDTH,
183 (x+1) * TILE_WIDTH,
184 MappableComponent::Boundary::Type::danger);
185 }
186 }
187}
diff --git a/src/systems/mapping.h b/src/systems/mapping.h new file mode 100644 index 0000000..3c47419 --- /dev/null +++ b/src/systems/mapping.h
@@ -0,0 +1,19 @@
1#ifndef MAPPING_H_33FC2294
2#define MAPPING_H_33FC2294
3
4#include "system.h"
5
6class MappingSystem : public System {
7public:
8
9 MappingSystem(Game& game) : System(game)
10 {
11 }
12
13 void render(Texture& texture);
14
15 void generateBoundaries(id_type mapEntity);
16
17};
18
19#endif /* end of include guard: MAPPING_H_33FC2294 */
diff --git a/src/systems/orienting.cpp b/src/systems/orienting.cpp new file mode 100644 index 0000000..d73ddd2 --- /dev/null +++ b/src/systems/orienting.cpp
@@ -0,0 +1,230 @@
1#include "orienting.h"
2#include "game.h"
3#include "components/orientable.h"
4#include "components/ponderable.h"
5#include "systems/animating.h"
6#include "consts.h"
7#include "muxer.h"
8
9void OrientingSystem::tick(double)
10{
11 auto entities = game_.getEntityManager().getEntitiesWithComponents<
12 OrientableComponent,
13 PonderableComponent>();
14
15 for (id_type entity : entities)
16 {
17 auto& orientable = game_.getEntityManager().
18 getComponent<OrientableComponent>(entity);
19
20 auto& ponderable = game_.getEntityManager().
21 getComponent<PonderableComponent>(entity);
22
23 switch (orientable.getWalkState())
24 {
25 case OrientableComponent::WalkState::still:
26 {
27 ponderable.vel.x() = 0.0;
28
29 break;
30 }
31
32 case OrientableComponent::WalkState::left:
33 {
34 ponderable.vel.x() = -WALK_SPEED;
35
36 break;
37 }
38
39 case OrientableComponent::WalkState::right:
40 {
41 ponderable.vel.x() = WALK_SPEED;
42
43 break;
44 }
45 }
46
47 if (orientable.isJumping() && (ponderable.vel.y() > 0))
48 {
49 orientable.setJumping(false);
50 }
51 }
52}
53
54void OrientingSystem::moveLeft(id_type entity)
55{
56 auto& ponderable = game_.getEntityManager().
57 getComponent<PonderableComponent>(entity);
58
59 auto& orientable = game_.getEntityManager().
60 getComponent<OrientableComponent>(entity);
61
62 orientable.setFacingRight(false);
63 orientable.setWalkState(OrientableComponent::WalkState::left);
64
65 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
66 if (ponderable.grounded)
67 {
68 animating.startAnimation(entity, "walkingLeft");
69 } else {
70 animating.startAnimation(entity, "stillLeft");
71 }
72}
73
74void OrientingSystem::moveRight(id_type entity)
75{
76 auto& ponderable = game_.getEntityManager().
77 getComponent<PonderableComponent>(entity);
78
79 auto& orientable = game_.getEntityManager().
80 getComponent<OrientableComponent>(entity);
81
82 orientable.setFacingRight(true);
83 orientable.setWalkState(OrientableComponent::WalkState::right);
84
85 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
86 if (ponderable.grounded)
87 {
88 animating.startAnimation(entity, "walkingRight");
89 } else {
90 animating.startAnimation(entity, "stillRight");
91 }
92}
93
94void OrientingSystem::stopWalking(id_type entity)
95{
96 auto& orientable = game_.getEntityManager().
97 getComponent<OrientableComponent>(entity);
98
99 orientable.setWalkState(OrientableComponent::WalkState::still);
100
101 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
102
103 if (orientable.isFacingRight())
104 {
105 animating.startAnimation(entity, "stillRight");
106 } else {
107 animating.startAnimation(entity, "stillLeft");
108 }
109}
110
111void OrientingSystem::jump(id_type entity)
112{
113 auto& ponderable = game_.getEntityManager().
114 getComponent<PonderableComponent>(entity);
115
116 if (ponderable.grounded)
117 {
118 auto& orientable = game_.getEntityManager().
119 getComponent<OrientableComponent>(entity);
120
121 orientable.setJumping(true);
122
123 playSound("res/Randomize87.wav", 0.25);
124
125 ponderable.vel.y() = JUMP_VELOCITY;
126 ponderable.accel.y() = JUMP_GRAVITY;
127
128 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
129 if (orientable.isFacingRight())
130 {
131 animating.startAnimation(entity, "stillRight");
132 } else {
133 animating.startAnimation(entity, "stillLeft");
134 }
135 }
136}
137
138void OrientingSystem::stopJumping(id_type entity)
139{
140 auto& orientable = game_.getEntityManager().
141 getComponent<OrientableComponent>(entity);
142
143 if (orientable.isJumping())
144 {
145 orientable.setJumping(false);
146
147 auto& ponderable = game_.getEntityManager().
148 getComponent<PonderableComponent>(entity);
149
150 ponderable.accel.y() = NORMAL_GRAVITY;
151 }
152}
153
154void OrientingSystem::land(id_type entity)
155{
156 auto& orientable = game_.getEntityManager().
157 getComponent<OrientableComponent>(entity);
158
159 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
160
161 switch (orientable.getWalkState())
162 {
163 case OrientableComponent::WalkState::still:
164 {
165 if (orientable.isFacingRight())
166 {
167 animating.startAnimation(entity, "stillRight");
168 } else {
169 animating.startAnimation(entity, "stillLeft");
170 }
171
172 break;
173 }
174
175 case OrientableComponent::WalkState::left:
176 {
177 animating.startAnimation(entity, "walkingLeft");
178
179 break;
180 }
181
182 case OrientableComponent::WalkState::right:
183 {
184 animating.startAnimation(entity, "walkingRight");
185
186 break;
187 }
188 }
189}
190
191void OrientingSystem::startFalling(id_type entity)
192{
193 auto& orientable = game_.getEntityManager().
194 getComponent<OrientableComponent>(entity);
195
196 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
197
198 if (orientable.isFacingRight())
199 {
200 animating.startAnimation(entity, "stillRight");
201 } else {
202 animating.startAnimation(entity, "stillLeft");
203 }
204}
205
206void OrientingSystem::drop(id_type entity)
207{
208 auto& orientable = game_.getEntityManager().
209 getComponent<OrientableComponent>(entity);
210
211 auto& ponderable = game_.getEntityManager().
212 getComponent<PonderableComponent>(entity);
213
214 if (ponderable.grounded
215 && (orientable.getDropState() == OrientableComponent::DropState::none))
216 {
217 orientable.setDropState(OrientableComponent::DropState::ready);
218 }
219}
220
221void OrientingSystem::stopDropping(id_type entity)
222{
223 auto& orientable = game_.getEntityManager().
224 getComponent<OrientableComponent>(entity);
225
226 if (orientable.getDropState() == OrientableComponent::DropState::ready)
227 {
228 orientable.setDropState(OrientableComponent::DropState::none);
229 }
230}
diff --git a/src/systems/orienting.h b/src/systems/orienting.h new file mode 100644 index 0000000..4ded612 --- /dev/null +++ b/src/systems/orienting.h
@@ -0,0 +1,35 @@
1#ifndef ORIENTING_H_099F0C23
2#define ORIENTING_H_099F0C23
3
4#include "system.h"
5
6class OrientingSystem : public System {
7public:
8
9 OrientingSystem(Game& game) : System(game)
10 {
11 }
12
13 void tick(double dt);
14
15 void moveLeft(id_type entity);
16
17 void moveRight(id_type entity);
18
19 void stopWalking(id_type entity);
20
21 void jump(id_type entity);
22
23 void stopJumping(id_type entity);
24
25 void land(id_type entity);
26
27 void startFalling(id_type entity);
28
29 void drop(id_type entity);
30
31 void stopDropping(id_type entity);
32
33};
34
35#endif /* end of include guard: ORIENTING_H_099F0C23 */
diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp new file mode 100644 index 0000000..6652099 --- /dev/null +++ b/src/systems/playing.cpp
@@ -0,0 +1,144 @@
1#include "playing.h"
2#include "game.h"
3#include "components/transformable.h"
4#include "components/animatable.h"
5#include "components/playable.h"
6#include "components/controllable.h"
7#include "components/orientable.h"
8#include "systems/mapping.h"
9#include "systems/pondering.h"
10#include "systems/orienting.h"
11#include "systems/scheduling.h"
12#include "systems/controlling.h"
13#include "systems/animating.h"
14#include "systems/realizing.h"
15#include "animation.h"
16#include "muxer.h"
17
18void PlayingSystem::initPlayer()
19{
20 id_type player = game_.getEntityManager().emplaceEntity();
21
22 AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6};
23 playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1);
24 playerGraphics.emplaceAnimation("stillRight", 0, 1, 1);
25 playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10);
26 playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10);
27
28 game_.getEntityManager().emplaceComponent<AnimatableComponent>(
29 player,
30 std::move(playerGraphics));
31
32 game_.getSystemManager().getSystem<AnimatingSystem>().startAnimation(
33 player,
34 "stillLeft");
35
36 auto& realizing = game_.getSystemManager().getSystem<RealizingSystem>();
37
38 auto& transformable = game_.getEntityManager().
39 emplaceComponent<TransformableComponent>(player);
40
41 transformable.pos = realizing.getStartingPos();
42 transformable.size.w() = 10;
43 transformable.size.h() = 12;
44
45 game_.getSystemManager().getSystem<PonderingSystem>().initializeBody(
46 player,
47 PonderableComponent::Type::freefalling);
48
49 game_.getEntityManager().emplaceComponent<ControllableComponent>(player);
50 game_.getEntityManager().emplaceComponent<OrientableComponent>(player);
51
52 auto& playable = game_.getEntityManager().
53 emplaceComponent<PlayableComponent>(player);
54
55 playable.mapId = realizing.getActiveMap();
56 playable.checkpointMapId = realizing.getStartingMapId();
57 playable.checkpointPos = realizing.getStartingPos();
58
59 realizing.enterActiveMap(player);
60
61 realizing.setActivePlayer(player);
62}
63
64void PlayingSystem::changeMap(
65 id_type player,
66 size_t mapId,
67 vec2d warpPos)
68{
69 auto& playable = game_.getEntityManager().
70 getComponent<PlayableComponent>(player);
71
72 auto& transformable = game_.getEntityManager().
73 getComponent<TransformableComponent>(player);
74
75 auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>();
76 auto& realizing = game_.getSystemManager().getSystem<RealizingSystem>();
77
78 id_type newMapEntity = realizing.getEntityByMapId(mapId);
79
80 if (playable.mapId != newMapEntity)
81 {
82 if (playable.mapId == realizing.getActiveMap())
83 {
84 realizing.leaveActiveMap(player);
85 } else if (newMapEntity == realizing.getActiveMap())
86 {
87 realizing.enterActiveMap(player);
88 }
89
90 playable.mapId = newMapEntity;
91 }
92
93 pondering.unferry(player);
94
95 transformable.pos = warpPos;
96
97 if (realizing.getActivePlayer() == player)
98 {
99 realizing.loadMap(newMapEntity);
100 }
101}
102
103void PlayingSystem::die(id_type player)
104{
105 playSound("res/Hit_Hurt5.wav", 0.25);
106
107 auto& animatable = game_.getEntityManager().
108 getComponent<AnimatableComponent>(player);
109
110 auto& ponderable = game_.getEntityManager().
111 getComponent<PonderableComponent>(player);
112
113 auto& controlling = game_.getSystemManager().getSystem<ControllingSystem>();
114 controlling.freeze(player);
115
116 animatable.frozen = true;
117 animatable.flickering = true;
118 ponderable.frozen = true;
119 ponderable.collidable = false;
120
121 auto& scheduling = game_.getSystemManager().getSystem<SchedulingSystem>();
122
123 scheduling.schedule(player, 0.75, [&] (id_type player) {
124 auto& playable = game_.getEntityManager().
125 getComponent<PlayableComponent>(player);
126
127 changeMap(
128 player,
129 playable.checkpointMapId,
130 playable.checkpointPos);
131
132 animatable.frozen = false;
133 animatable.flickering = false;
134 ponderable.frozen = false;
135 ponderable.collidable = true;
136
137 // Reset the walk state, and then potentially let the
138 // ControllingSystem set it again.
139 auto& orienting = game_.getSystemManager().getSystem<OrientingSystem>();
140 orienting.stopWalking(player);
141
142 controlling.unfreeze(player);
143 });
144}
diff --git a/src/systems/playing.h b/src/systems/playing.h new file mode 100644 index 0000000..31f79ab --- /dev/null +++ b/src/systems/playing.h
@@ -0,0 +1,25 @@
1#ifndef PLAYING_H_70A54F7D
2#define PLAYING_H_70A54F7D
3
4#include "system.h"
5#include "vector.h"
6
7class PlayingSystem : public System {
8public:
9
10 PlayingSystem(Game& game) : System(game)
11 {
12 }
13
14 void initPlayer();
15
16 void changeMap(
17 id_type player,
18 size_t mapId,
19 vec2d warpPos);
20
21 void die(id_type player);
22
23};
24
25#endif /* end of include guard: PLAYING_H_70A54F7D */
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp new file mode 100644 index 0000000..d841679 --- /dev/null +++ b/src/systems/pondering.cpp
@@ -0,0 +1,916 @@
1#include "pondering.h"
2#include <queue>
3#include <algorithm>
4#include "game.h"
5#include "components/ponderable.h"
6#include "components/transformable.h"
7#include "components/orientable.h"
8#include "components/mappable.h"
9#include "components/playable.h"
10#include "systems/orienting.h"
11#include "systems/playing.h"
12#include "systems/realizing.h"
13#include "systems/scripting.h"
14#include "consts.h"
15
16void PonderingSystem::tick(double dt)
17{
18 auto entities = game_.getEntityManager().getEntitiesWithComponents<
19 PonderableComponent,
20 TransformableComponent>();
21
22 for (id_type entity : entities)
23 {
24 auto& ponderable = game_.getEntityManager().
25 getComponent<PonderableComponent>(entity);
26
27 // We will recursively process ferried bodies after their ferries have been
28 // processed, so hold off on processing ferried bodies at the top level.
29 if (ponderable.ferried)
30 {
31 continue;
32 }
33
34 tickBody(entity, dt);
35 }
36}
37
38void PonderingSystem::initializeBody(
39 id_type entity,
40 PonderableComponent::Type type)
41{
42 auto& ponderable = game_.getEntityManager().
43 emplaceComponent<PonderableComponent>(entity, type);
44
45 if (type == PonderableComponent::Type::freefalling)
46 {
47 ponderable.accel.y() = NORMAL_GRAVITY;
48 }
49}
50
51void PonderingSystem::initPrototype(id_type prototype)
52{
53 auto& ponderable = game_.getEntityManager().
54 getComponent<PonderableComponent>(prototype);
55
56 ponderable.vel.x() = 0.0;
57 ponderable.vel.y() = 0.0;
58 ponderable.accel.x() = 0.0;
59 ponderable.accel.y() = 0.0;
60 ponderable.grounded = false;
61 ponderable.frozen = false;
62 ponderable.collidable = true;
63 ponderable.ferried = false;
64 ponderable.passengers.clear();
65}
66
67void PonderingSystem::unferry(id_type entity)
68{
69 auto& ponderable = game_.getEntityManager().
70 getComponent<PonderableComponent>(entity);
71
72 if (ponderable.ferried)
73 {
74 ponderable.ferried = false;
75
76 auto& ferryPonder = game_.getEntityManager().
77 getComponent<PonderableComponent>(ponderable.ferry);
78
79 ferryPonder.passengers.erase(entity);
80 }
81}
82
83void PonderingSystem::tickBody(
84 id_type entity,
85 double dt)
86{
87 auto& ponderable = game_.getEntityManager().
88 getComponent<PonderableComponent>(entity);
89
90 if (!ponderable.active)
91 {
92 return;
93 }
94
95 auto& transformable = game_.getEntityManager().
96 getComponent<TransformableComponent>(entity);
97
98 // Accelerate
99 if (!ponderable.frozen)
100 {
101 ponderable.vel += ponderable.accel * dt;
102
103 if ((ponderable.type == PonderableComponent::Type::freefalling)
104 && (ponderable.vel.y() > TERMINAL_VELOCITY))
105 {
106 ponderable.vel.y() = TERMINAL_VELOCITY;
107 }
108 }
109
110 // Move
111 vec2d newPos = transformable.pos;
112
113 if (!ponderable.frozen)
114 {
115 newPos += ponderable.vel * dt;
116 }
117
118 CollisionResult result = moveBody(entity, newPos);
119
120 // Perform cleanup for orientable entites
121 bool groundedChanged = (ponderable.grounded != result.grounded);
122 ponderable.grounded = result.grounded;
123
124 if (game_.getEntityManager().hasComponent<OrientableComponent>(entity))
125 {
126 auto& orientable = game_.getEntityManager().
127 getComponent<OrientableComponent>(entity);
128
129 // Handle changes in groundedness
130 if (groundedChanged)
131 {
132 if (ponderable.grounded)
133 {
134 game_.getSystemManager().getSystem<OrientingSystem>().land(entity);
135 } else {
136 game_.getSystemManager().
137 getSystem<OrientingSystem>().startFalling(entity);
138 }
139 }
140
141 // Complete dropping, if necessary
142 if (orientable.getDropState() == OrientableComponent::DropState::active)
143 {
144 orientable.setDropState(OrientableComponent::DropState::none);
145 }
146 }
147
148 // Ferry or unferry as necessary
149 if ((ponderable.type == PonderableComponent::Type::freefalling) &&
150 groundedChanged)
151 {
152 if (ponderable.grounded &&
153 game_.getEntityManager().
154 hasComponent<PonderableComponent>(result.groundEntity))
155 {
156 // The body is now being ferried
157 auto& ferryPonder = game_.getEntityManager().
158 getComponent<PonderableComponent>(result.groundEntity);
159
160 ponderable.ferried = true;
161 ponderable.ferry = result.groundEntity;
162 ponderable.ferrySide = Direction::up;
163
164 ferryPonder.passengers.insert(entity);
165 } else if (ponderable.ferried)
166 {
167 // The body is no longer being ferried
168 unferry(entity);
169 }
170 }
171
172 // Handle ferry passengers
173 std::set<id_type> passengers = ponderable.passengers;
174
175 for (id_type passenger : passengers)
176 {
177 tickBody(passenger, dt);
178 }
179}
180
181PonderingSystem::CollisionResult PonderingSystem::moveBody(
182 id_type entity,
183 vec2d newPos)
184{
185 auto& ponderable = game_.getEntityManager().
186 getComponent<PonderableComponent>(entity);
187
188 CollisionResult result;
189
190 if (ponderable.collidable)
191 {
192 result = detectCollisions(entity, newPos);
193 } else {
194 result.pos = newPos;
195 }
196
197 if (!ponderable.frozen)
198 {
199 auto& transformable = game_.getEntityManager().
200 getComponent<TransformableComponent>(entity);
201
202 vec2d delta = result.pos - transformable.pos;
203
204 // Move.
205 transformable.pos = result.pos;
206
207 // Stop if the entity hit a wall.
208 if (result.blockedHoriz)
209 {
210 ponderable.vel.x() = 0.0;
211 }
212
213 if (result.blockedVert)
214 {
215 ponderable.vel.y() = 0.0;
216 }
217
218 // Move ferry passengers by the appropriate amount.
219 auto passengers = ponderable.passengers;
220
221 for (id_type passenger : passengers)
222 {
223 auto& passTrans = game_.getEntityManager().
224 getComponent<TransformableComponent>(passenger);
225
226 moveBody(passenger, passTrans.pos + delta);
227 }
228
229 // Move to an adjacent map, if necessary
230 if (result.adjacentlyWarping)
231 {
232 vec2d warpPos = result.pos;
233
234 switch (result.adjWarpDir)
235 {
236 case Direction::left:
237 {
238 warpPos.x() = GAME_WIDTH + WALL_GAP - transformable.size.w();
239
240 break;
241 }
242
243 case Direction::right:
244 {
245 warpPos.x() = -WALL_GAP;
246
247 break;
248 }
249
250 case Direction::up:
251 {
252 warpPos.y() = MAP_HEIGHT * TILE_HEIGHT - transformable.size.h();
253
254 break;
255 }
256
257 case Direction::down:
258 {
259 warpPos.y() = -WALL_GAP;
260
261 break;
262 }
263 }
264
265 game_.getSystemManager().getSystem<PlayingSystem>().
266 changeMap(
267 entity,
268 result.adjWarpMapId,
269 warpPos);
270 }
271 }
272
273 return result;
274}
275
276namespace CollisionParams {
277
278 template <typename HorizVert>
279 class Desc : public HorizVert {
280 public:
281
282 inline static bool AtLeastInAxisSweep(
283 double boundaryAxis,
284 double entityAxis)
285 {
286 return (boundaryAxis >= entityAxis);
287 }
288
289 inline static bool IsPastAxis(
290 double colliderAxis,
291 double entityAxis)
292 {
293 return (colliderAxis > entityAxis);
294 }
295
296 inline static double EntityAxis(const vec2d& pos, const vec2i& size)
297 {
298 return HorizVert::AxisLower(pos);
299 }
300
301 inline static double ObjectAxis(const vec2d& pos, const vec2i& size)
302 {
303 return HorizVert::AxisUpper(pos, size);
304 }
305
306 inline static bool Closer(double left, double right)
307 {
308 return right < left;
309 }
310 };
311
312 template <typename HorizVert>
313 class Asc : public HorizVert {
314 public:
315
316 inline static bool AtLeastInAxisSweep(
317 double boundaryAxis,
318 double entityAxis)
319 {
320 return (boundaryAxis <= entityAxis);
321 }
322
323 inline static bool IsPastAxis(
324 double colliderAxis,
325 double entityAxis)
326 {
327 return (colliderAxis < entityAxis);
328 }
329
330 inline static double EntityAxis(const vec2d& pos, const vec2i& size)
331 {
332 return HorizVert::AxisUpper(pos, size);
333 }
334
335 inline static double ObjectAxis(const vec2d& pos, const vec2i& size)
336 {
337 return HorizVert::AxisLower(pos);
338 }
339
340 inline static bool Closer(double left, double right)
341 {
342 return left < right;
343 }
344 };
345
346 template <size_t Axis, size_t NonAxis>
347 class HorizVert {
348 public:
349
350 inline static double AxisLower(const vec2d& pos)
351 {
352 return pos.coords[Axis];
353 }
354
355 inline static double AxisUpper(const vec2d& pos, const vec2i& size)
356 {
357 return pos.coords[Axis] + size.coords[Axis];
358 }
359
360 inline static double NonAxisLower(const vec2d& pos)
361 {
362 return pos.coords[NonAxis];
363 }
364
365 inline static double NonAxisUpper(const vec2d& pos, const vec2i& size)
366 {
367 return pos.coords[NonAxis] + size.coords[NonAxis];
368 }
369
370 };
371
372 using Horizontal = HorizVert<0, 1>;
373 using Vertical = HorizVert<1, 0>;
374
375 template <Direction dir, typename AscDesc>
376 class DetectCollisions : public AscDesc {
377 public:
378
379 static const Direction Dir = dir;
380
381 inline static double EntityAxis(const vec2d& pos, const vec2i& size)
382 {
383 return AscDesc::EntityAxis(pos, size);
384 }
385
386 inline static double ObjectAxis(const vec2d& pos, const vec2i& size)
387 {
388 return AscDesc::ObjectAxis(pos, size);
389 }
390
391 inline static double EntityAxis(const TransformableComponent& transformable)
392 {
393 return AscDesc::EntityAxis(transformable.pos, transformable.size);
394 }
395
396 inline static double ObjectAxis(const TransformableComponent& transformable)
397 {
398 return AscDesc::ObjectAxis(transformable.pos, transformable.size);
399 }
400 };
401
402 class Left : public DetectCollisions<Direction::left, Desc<Horizontal>> {
403 public:
404
405 inline static const MappableComponent::desc_boundaries_type& MapBoundaries(
406 const MappableComponent& mappable)
407 {
408 return mappable.leftBoundaries;
409 }
410 };
411
412 class Right : public DetectCollisions<Direction::right, Asc<Horizontal>> {
413 public:
414
415 inline static const MappableComponent::asc_boundaries_type& MapBoundaries(
416 const MappableComponent& mappable)
417 {
418 return mappable.rightBoundaries;
419 }
420 };
421
422 class Up : public DetectCollisions<Direction::up, Desc<Vertical>> {
423 public:
424
425 inline static const MappableComponent::desc_boundaries_type& MapBoundaries(
426 const MappableComponent& mappable)
427 {
428 return mappable.upBoundaries;
429 }
430 };
431
432 class Down : public DetectCollisions<Direction::down, Asc<Vertical>> {
433 public:
434
435 inline static const MappableComponent::asc_boundaries_type& MapBoundaries(
436 const MappableComponent& mappable)
437 {
438 return mappable.downBoundaries;
439 }
440 };
441};
442
443PonderingSystem::CollisionResult PonderingSystem::detectCollisions(
444 id_type entity,
445 vec2d newPos)
446{
447 auto& transformable = game_.getEntityManager().
448 getComponent<TransformableComponent>(entity);
449
450 CollisionResult result;
451 result.pos.x() = newPos.x();
452 result.pos.y() = transformable.pos.y();
453
454 // Find horizontal collisions.
455 if (result.pos.x() < transformable.pos.x())
456 {
457 detectCollisionsInDirection<CollisionParams::Left>(entity, result);
458 } else if (result.pos.x() > transformable.pos.x())
459 {
460 detectCollisionsInDirection<CollisionParams::Right>(entity, result);
461 }
462
463 // Find vertical collisions
464 if (!result.stopProcessing)
465 {
466 result.pos.y() = newPos.y();
467 result.touchedWall = false;
468
469 if (result.pos.y() < transformable.pos.y())
470 {
471 detectCollisionsInDirection<CollisionParams::Up>(entity, result);
472 } else if (result.pos.y() > transformable.pos.y())
473 {
474 detectCollisionsInDirection<CollisionParams::Down>(entity, result);
475 }
476 }
477
478 return result;
479}
480
481template <typename Param>
482void PonderingSystem::detectCollisionsInDirection(
483 id_type entity,
484 CollisionResult& result)
485{
486 // Get map data.
487 id_type mapEntity =
488 game_.getSystemManager().getSystem<RealizingSystem>().getActiveMap();
489
490 auto& mappable = game_.getEntityManager().
491 getComponent<MappableComponent>(mapEntity);
492
493 // Get old location.
494 auto& transform = game_.getEntityManager().
495 getComponent<TransformableComponent>(entity);
496
497 auto& ponderable = game_.getEntityManager().
498 getComponent<PonderableComponent>(entity);
499
500 bool boundaryCollision = false;
501
502 auto boundaries = Param::MapBoundaries(mappable);
503 auto it = boundaries.lower_bound(Param::EntityAxis(transform));
504
505 // Find the axis distance of the closest environmental boundary.
506 for (;
507 (it != std::end(boundaries)) &&
508 Param::AtLeastInAxisSweep(
509 it->first,
510 Param::EntityAxis(result.pos, transform.size));
511 it++)
512 {
513 // Check that the boundary is in range for the other axis.
514 if ((Param::NonAxisUpper(result.pos, transform.size) > it->second.lower) &&
515 (Param::NonAxisLower(result.pos) < it->second.upper))
516 {
517 // We have a collision!
518 boundaryCollision = true;
519
520 break;
521 }
522 }
523
524 // Find the results of pretending to move the entity's passengers, if there
525 // are any.
526 vec2d delta = result.pos - transform.pos;
527 std::map<id_type, CollisionResult> passResults;
528
529 for (id_type passenger : ponderable.passengers)
530 {
531 auto& passPonder = game_.getEntityManager().
532 getComponent<PonderableComponent>(passenger);
533
534 if (passPonder.ferrySide == Param::Dir)
535 {
536 auto& passTrans = game_.getEntityManager().
537 getComponent<TransformableComponent>(passenger);
538
539 passResults[passenger] =
540 detectCollisions(passenger, passTrans.pos + delta);
541 }
542 }
543
544 // Find a list of potential colliders, sorted so that the closest is
545 // first.
546 std::vector<id_type> colliders;
547
548 auto entities = game_.getEntityManager().getEntitiesWithComponents<
549 PonderableComponent,
550 TransformableComponent>();
551
552 for (id_type collider : entities)
553 {
554 // Can't collide with self.
555 if (collider == entity)
556 {
557 continue;
558 }
559
560 auto& colliderPonder = game_.getEntityManager().
561 getComponent<PonderableComponent>(collider);
562
563 // Only check objects that are active and collidable.
564 if (!colliderPonder.active || !colliderPonder.collidable)
565 {
566 continue;
567 }
568
569 // If the collider is a passenger of the entity, pretend that it has already
570 // moved.
571 auto& colliderTrans = game_.getEntityManager().
572 getComponent<TransformableComponent>(collider);
573
574 vec2d colliderPos = colliderTrans.pos;
575 vec2i colliderSize = colliderTrans.size;
576
577 if (passResults.count(collider))
578 {
579 colliderPos = passResults[collider].pos;
580 }
581
582 // Check if the entity would move into the potential collider,
583 if (Param::IsPastAxis(
584 Param::ObjectAxis(colliderPos, colliderSize),
585 Param::EntityAxis(result.pos, transform.size)) &&
586 // that it wasn't already colliding,
587 !Param::IsPastAxis(
588 Param::ObjectAxis(colliderPos, colliderSize),
589 Param::EntityAxis(transform)) &&
590 // that the position on the non-axis is in range,
591 (Param::NonAxisUpper(colliderPos, colliderSize) >
592 Param::NonAxisLower(result.pos)) &&
593 (Param::NonAxisLower(colliderPos) <
594 Param::NonAxisUpper(result.pos, transform.size)) &&
595 // and that the collider is not farther away than the environmental
596 // boundary.
597 (!boundaryCollision ||
598 Param::AtLeastInAxisSweep(
599 Param::ObjectAxis(colliderPos, colliderSize),
600 it->first)))
601 {
602 colliders.push_back(collider);
603 }
604 }
605
606 // Sort the potential colliders such that the closest to the axis of movement
607 // is first. When sorting, treat passengers of the entity as having already
608 // moved.
609 std::sort(
610 std::begin(colliders),
611 std::end(colliders),
612 [&] (id_type left, id_type right) {
613 auto& leftTrans = game_.getEntityManager().
614 getComponent<TransformableComponent>(left);
615
616 vec2d leftPos = leftTrans.pos;
617
618 if (passResults.count(left))
619 {
620 leftPos = passResults[left].pos;
621 }
622
623 auto& rightTrans = game_.getEntityManager().
624 getComponent<TransformableComponent>(right);
625
626 vec2d rightPos = rightTrans.pos;
627
628 if (passResults.count(right))
629 {
630 rightPos = passResults[right].pos;
631 }
632
633 return Param::Closer(
634 Param::ObjectAxis(leftPos, leftTrans.size),
635 Param::ObjectAxis(rightPos, rightTrans.size));
636 });
637
638 for (id_type collider : colliders)
639 {
640 auto& colliderTrans = game_.getEntityManager().
641 getComponent<TransformableComponent>(collider);
642
643 // If the collider is a passenger of the entity, pretend that it has already
644 // moved.
645 vec2d colliderPos = colliderTrans.pos;
646 vec2i colliderSize = colliderTrans.size;
647
648 if (passResults.count(collider))
649 {
650 colliderPos = passResults[collider].pos;
651 }
652
653 // Check if the entity would still move into the potential collider.
654 if (!Param::IsPastAxis(
655 Param::ObjectAxis(colliderPos, colliderSize),
656 Param::EntityAxis(result.pos, transform.size)))
657 {
658 break;
659 }
660
661 // TODO: Check if the entity is moving into one of its passengers.
662 auto& colliderPonder = game_.getEntityManager().
663 getComponent<PonderableComponent>(collider);
664
665 processCollision(
666 entity,
667 collider,
668 Param::Dir,
669 colliderPonder.colliderType,
670 Param::ObjectAxis(colliderPos, colliderSize),
671 Param::NonAxisLower(colliderPos),
672 Param::NonAxisUpper(colliderPos, colliderSize),
673 result);
674
675 if (result.stopProcessing)
676 {
677 break;
678 }
679 }
680
681 // If movement hasn't been stopped by an intermediary object, and
682 // collision checking hasn't been stopped, process the environmental
683 // boundaries closest to the entity.
684 if (!result.stopProcessing && !result.touchedWall && boundaryCollision)
685 {
686 double boundaryAxis = it->first;
687
688 for (;
689 (it != std::end(boundaries)) &&
690 (it->first == boundaryAxis);
691 it++)
692 {
693 if ((Param::NonAxisLower(result.pos) < it->second.upper) &&
694 (Param::NonAxisUpper(result.pos, transform.size) > it->second.lower))
695 {
696 processCollision(
697 entity,
698 mapEntity,
699 Param::Dir,
700 it->second.type,
701 it->first,
702 it->second.lower,
703 it->second.upper,
704 result);
705
706 if (result.stopProcessing)
707 {
708 break;
709 }
710 }
711 }
712 }
713}
714
715void PonderingSystem::processCollision(
716 id_type entity,
717 id_type collider,
718 Direction dir,
719 PonderableComponent::Collision type,
720 double axis,
721 double lower,
722 double upper,
723 CollisionResult& result)
724{
725 auto& transformable = game_.getEntityManager().
726 getComponent<TransformableComponent>(entity);
727
728 switch (type)
729 {
730 case PonderableComponent::Collision::wall:
731 {
732 result.touchedWall = true;
733
734 break;
735 }
736
737 case PonderableComponent::Collision::platform:
738 {
739 if (game_.getEntityManager().
740 hasComponent<OrientableComponent>(entity))
741 {
742 auto& orientable = game_.getEntityManager().
743 getComponent<OrientableComponent>(entity);
744
745 if (orientable.getDropState() !=
746 OrientableComponent::DropState::none)
747 {
748 orientable.setDropState(OrientableComponent::DropState::active);
749 } else {
750 result.touchedWall = true;
751 }
752 } else {
753 result.touchedWall = true;
754 }
755
756 break;
757 }
758
759 case PonderableComponent::Collision::adjacency:
760 {
761 auto& mappable = game_.getEntityManager().
762 getComponent<MappableComponent>(collider);
763
764 auto& adj = [&] () -> const MappableComponent::Adjacent& {
765 switch (dir)
766 {
767 case Direction::left: return mappable.leftAdjacent;
768 case Direction::right: return mappable.rightAdjacent;
769 case Direction::up: return mappable.upAdjacent;
770 case Direction::down: return mappable.downAdjacent;
771 }
772 }();
773
774 switch (adj.type)
775 {
776 case MappableComponent::Adjacent::Type::wall:
777 {
778 result.touchedWall = true;
779
780 break;
781 }
782
783 case MappableComponent::Adjacent::Type::wrap:
784 {
785 switch (dir)
786 {
787 case Direction::left:
788 {
789 result.pos.x() = GAME_WIDTH + WALL_GAP - transformable.size.w();
790
791 break;
792 }
793
794 case Direction::right:
795 {
796 result.pos.x() = -WALL_GAP;
797
798 break;
799 }
800
801 case Direction::up:
802 {
803 result.pos.y() =
804 MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.pos.h();
805
806 break;
807 }
808
809 case Direction::down:
810 {
811 result.pos.y() = -WALL_GAP;
812
813 break;
814 }
815 }
816 }
817
818 case MappableComponent::Adjacent::Type::warp:
819 {
820 if (game_.getEntityManager().
821 hasComponent<PlayableComponent>(entity))
822 {
823 result.adjacentlyWarping = true;
824 result.adjWarpDir = dir;
825 result.adjWarpMapId = adj.mapId;
826 }
827
828 break;
829 }
830
831 case MappableComponent::Adjacent::Type::reverse:
832 {
833 // TODO: not yet implemented.
834
835 break;
836 }
837 }
838
839 break;
840 }
841
842 case PonderableComponent::Collision::danger:
843 {
844 if (game_.getEntityManager().
845 hasComponent<PlayableComponent>(entity))
846 {
847 game_.getSystemManager().getSystem<PlayingSystem>().die(entity);
848
849 result.adjacentlyWarping = false;
850 }
851
852 result.stopProcessing = true;
853
854 break;
855 }
856
857 case PonderableComponent::Collision::event:
858 {
859 if (game_.getEntityManager().
860 hasComponent<PlayableComponent>(entity))
861 {
862 game_.getSystemManager().getSystem<ScriptingSystem>().
863 onTouch(collider, entity);
864 }
865
866 break;
867 }
868
869 default:
870 {
871 // Not yet implemented.
872
873 break;
874 }
875 }
876
877 if (!result.stopProcessing && result.touchedWall)
878 {
879 switch (dir)
880 {
881 case Direction::left:
882 {
883 result.pos.x() = axis;
884 result.blockedHoriz = true;
885
886 break;
887 }
888
889 case Direction::right:
890 {
891 result.pos.x() = axis - transformable.size.w();
892 result.blockedHoriz = true;
893
894 break;
895 }
896
897 case Direction::up:
898 {
899 result.pos.y() = axis;
900 result.blockedVert = true;
901
902 break;
903 }
904
905 case Direction::down:
906 {
907 result.pos.y() = axis - transformable.size.h();
908 result.blockedVert = true;
909 result.groundEntity = collider;
910 result.grounded = true;
911
912 break;
913 }
914 }
915 }
916}
diff --git a/src/systems/pondering.h b/src/systems/pondering.h new file mode 100644 index 0000000..c79bbf6 --- /dev/null +++ b/src/systems/pondering.h
@@ -0,0 +1,81 @@
1#ifndef PONDERING_H_F2530E0E
2#define PONDERING_H_F2530E0E
3
4#include "system.h"
5#include "components/ponderable.h"
6#include "direction.h"
7#include "vector.h"
8
9class PonderingSystem : public System {
10public:
11
12 PonderingSystem(Game& game) : System(game)
13 {
14 }
15
16 void tick(double dt);
17
18 void initializeBody(id_type entity, PonderableComponent::Type type);
19
20 /**
21 * Initializes a ponderable map object from its prototype data.
22 *
23 * @requires entity is ponderable
24 * @requires entity is a map object
25 */
26 void initPrototype(id_type prototype);
27
28 /**
29 * Unferries an entity if it is a passenger of another entity. Use before
30 * moving a ponderable entity outside the PonderingSystem.
31 *
32 * @requires entity is ponderable
33 */
34 void unferry(id_type entity);
35
36private:
37
38 struct CollisionResult
39 {
40 vec2d pos;
41 bool stopProcessing = false;
42 bool touchedWall = false;
43 bool blockedHoriz = false;
44 bool blockedVert = false;
45 bool adjacentlyWarping = false;
46 Direction adjWarpDir;
47 size_t adjWarpMapId;
48 bool grounded = false;
49 id_type groundEntity;
50 };
51
52 void tickBody(
53 id_type entity,
54 double dt);
55
56 CollisionResult moveBody(
57 id_type entity,
58 vec2d newPos);
59
60 CollisionResult detectCollisions(
61 id_type entity,
62 vec2d newPos);
63
64 template <typename Param>
65 void detectCollisionsInDirection(
66 id_type entity,
67 CollisionResult& result);
68
69 void processCollision(
70 id_type entity,
71 id_type collider,
72 Direction dir,
73 PonderableComponent::Collision type,
74 double axis,
75 double lower,
76 double upper,
77 CollisionResult& result);
78
79};
80
81#endif /* end of include guard: PONDERING_H_F2530E0E */
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp new file mode 100644 index 0000000..baacf5a --- /dev/null +++ b/src/systems/realizing.cpp
@@ -0,0 +1,438 @@
1#include "realizing.h"
2#include <stdexcept>
3#include <libxml/parser.h>
4#include <cstring>
5#include <map>
6#include "game.h"
7#include "consts.h"
8#include "animation.h"
9#include "components/mappable.h"
10#include "components/animatable.h"
11#include "components/playable.h"
12#include "components/ponderable.h"
13#include "components/transformable.h"
14#include "components/prototypable.h"
15#include "components/automatable.h"
16#include "systems/mapping.h"
17#include "systems/animating.h"
18#include "systems/pondering.h"
19#include "systems/scripting.h"
20
21inline xmlChar* getProp(xmlNodePtr node, const char* attr)
22{
23 xmlChar* key = xmlGetProp(node, reinterpret_cast<const xmlChar*>(attr));
24 if (key == nullptr)
25 {
26 throw std::invalid_argument("Error parsing world file");
27 }
28
29 return key;
30}
31
32// TODO: neither the XML doc nor any of the emplaced entities are properly
33// destroyed if this constructor throws an exception.
34RealizingSystem::RealizingSystem(
35 Game& game,
36 std::string worldFile,
37 std::string prototypeFile) :
38 System(game),
39 worldFile_(std::move(worldFile)),
40 prototypeFile_(std::move(prototypeFile))
41{
42 auto& mapping = game_.getSystemManager().getSystem<MappingSystem>();
43
44 xmlChar* key = nullptr;
45
46 // Create a mapping between prototype names and the XML trees defining them.
47 xmlDocPtr protoXml = xmlParseFile(prototypeFile_.c_str());
48 if (protoXml == nullptr)
49 {
50 throw std::invalid_argument("Cannot find prototypes file");
51 }
52
53 xmlNodePtr protoTop = xmlDocGetRootElement(protoXml);
54 if (protoTop == nullptr)
55 {
56 throw std::invalid_argument("Error parsing prototypes file");
57 }
58
59 if (xmlStrcmp(protoTop->name, reinterpret_cast<const xmlChar*>("entities")))
60 {
61 throw std::invalid_argument("Error parsing prototypes file");
62 }
63
64 std::map<std::string, xmlNodePtr> prototypes;
65
66 for (xmlNodePtr node = protoTop->xmlChildrenNode;
67 node != nullptr;
68 node = node->next)
69 {
70 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("entity")))
71 {
72 key = getProp(node, "id");
73 std::string prototypeId = reinterpret_cast<char*>(key);
74 xmlFree(key);
75
76 prototypes[prototypeId] = node;
77 }
78 }
79
80 // Create entities from the world definition.
81 xmlDocPtr doc = xmlParseFile(worldFile_.c_str());
82 if (doc == nullptr)
83 {
84 throw std::invalid_argument("Cannot find world file");
85 }
86
87 xmlNodePtr top = xmlDocGetRootElement(doc);
88 if (top == nullptr)
89 {
90 throw std::invalid_argument("Error parsing world file");
91 }
92
93 if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("world")))
94 {
95 throw std::invalid_argument("Error parsing world file");
96 }
97
98 key = getProp(top, "startx");
99 startingPos_.x() = atoi(reinterpret_cast<char*>(key));
100 xmlFree(key);
101
102 key = getProp(top, "starty");
103 startingPos_.y() = atoi(reinterpret_cast<char*>(key));
104 xmlFree(key);
105
106 key = getProp(top, "startmap");
107 startingMapId_ = atoi(reinterpret_cast<char*>(key));
108 xmlFree(key);
109
110 for (xmlNodePtr node = top->xmlChildrenNode;
111 node != nullptr;
112 node = node->next)
113 {
114 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("map")))
115 {
116 id_type map = game_.getEntityManager().emplaceEntity();
117
118 auto& mappable = game_.getEntityManager().
119 emplaceComponent<MappableComponent>(map,
120 Texture("res/tiles.png"),
121 Texture("res/font.bmp"));
122
123 key = getProp(node, "id");
124 mappable.mapId = atoi(reinterpret_cast<char*>(key));
125 xmlFree(key);
126
127 key = getProp(node, "title");
128 mappable.title = reinterpret_cast<char*>(key);
129 xmlFree(key);
130
131 for (xmlNodePtr mapNode = node->xmlChildrenNode;
132 mapNode != nullptr;
133 mapNode = mapNode->next)
134 {
135 if (!xmlStrcmp(
136 mapNode->name,
137 reinterpret_cast<const xmlChar*>("environment")))
138 {
139 key = xmlNodeGetContent(mapNode);
140 if (key == nullptr)
141 {
142 throw std::invalid_argument("Error parsing world file");
143 }
144
145 mappable.tiles.clear();
146 mappable.tiles.push_back(atoi(strtok(
147 reinterpret_cast<char*>(key),
148 ",\n")));
149
150 for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++)
151 {
152 mappable.tiles.push_back(atoi(strtok(nullptr, ",\n")));
153 }
154
155 xmlFree(key);
156 } else if (!xmlStrcmp(
157 mapNode->name,
158 reinterpret_cast<const xmlChar*>("entity")))
159 {
160 id_type mapObject = game_.getEntityManager().emplaceEntity();
161
162 key = getProp(mapNode, "type");
163 std::string prototypeId = reinterpret_cast<char*>(key);
164 xmlFree(key);
165
166 xmlNodePtr prototypeNode = prototypes[prototypeId];
167
168 // Set the coordinates from the object definition.
169 auto& transformable = game_.getEntityManager().
170 emplaceComponent<TransformableComponent>(mapObject);
171
172 key = getProp(mapNode, "x");
173 transformable.origPos.x() = atoi(reinterpret_cast<char*>(key));
174 xmlFree(key);
175
176 key = getProp(mapNode, "y");
177 transformable.origPos.y() = atoi(reinterpret_cast<char*>(key));
178 xmlFree(key);
179
180 // Set the sprite and size using the prototype definition.
181 key = getProp(prototypeNode, "sprite");
182 std::string spritePath = reinterpret_cast<char*>(key);
183 xmlFree(key);
184
185 key = getProp(prototypeNode, "width");
186 transformable.origSize.w() = atoi(reinterpret_cast<char*>(key));
187 xmlFree(key);
188
189 key = getProp(prototypeNode, "height");
190 transformable.origSize.h() = atoi(reinterpret_cast<char*>(key));
191 xmlFree(key);
192
193 AnimationSet objectAnim(
194 spritePath.c_str(),
195 transformable.origSize.w(),
196 transformable.origSize.h(),
197 1);
198
199 objectAnim.emplaceAnimation("static", 0, 1, 1);
200
201 auto& animatable = game_.getEntityManager().
202 emplaceComponent<AnimatableComponent>(
203 mapObject,
204 std::move(objectAnim));
205
206 animatable.origAnimation = "static";
207
208 // Create a physics body.
209 game_.getSystemManager().getSystem<PonderingSystem>().
210 initializeBody(mapObject, PonderableComponent::Type::vacuumed);
211
212
213
214
215
216 auto& prototypable = game_.getEntityManager().
217 emplaceComponent<PrototypableComponent>(mapObject);
218
219 prototypable.prototypeId = prototypeId;
220
221 key = getProp(mapNode, "index");
222 prototypable.mapObjectIndex = atoi(reinterpret_cast<char*>(key));
223 xmlFree(key);
224
225 if (prototypeId == "movplat")
226 {
227 auto& automatable = game_.getEntityManager().
228 emplaceComponent<AutomatableComponent>(mapObject);
229
230 automatable.table = prototypeId;
231 } else if (prototypeId == "checkpoint")
232 {
233 auto& ponderable = game_.getEntityManager().
234 getComponent<PonderableComponent>(mapObject);
235
236 ponderable.colliderType = PonderableComponent::Collision::event;
237 }
238
239 mappable.objects.push_back(mapObject);
240 } else if (!xmlStrcmp(
241 mapNode->name,
242 reinterpret_cast<const xmlChar*>("adjacent")))
243 {
244 key = getProp(mapNode, "type");
245 std::string adjTypeStr(reinterpret_cast<char*>(key));
246 xmlFree(key);
247
248 MappableComponent::Adjacent::Type adjType;
249 if (adjTypeStr == "wall")
250 {
251 adjType = MappableComponent::Adjacent::Type::wall;
252 } else if (adjTypeStr == "wrap")
253 {
254 adjType = MappableComponent::Adjacent::Type::wrap;
255 } else if (adjTypeStr == "warp")
256 {
257 adjType = MappableComponent::Adjacent::Type::warp;
258 } else if (adjTypeStr == "reverseWarp")
259 {
260 adjType = MappableComponent::Adjacent::Type::reverse;
261 } else {
262 throw std::logic_error("Invalid adjacency type");
263 }
264
265 key = getProp(mapNode, "map");
266 size_t adjMapId = atoi(reinterpret_cast<char*>(key));
267 xmlFree(key);
268
269 key = getProp(mapNode, "dir");
270 std::string adjDir(reinterpret_cast<char*>(key));
271 xmlFree(key);
272
273 if (adjDir == "left")
274 {
275 mappable.leftAdjacent = {adjType, adjMapId};
276 } else if (adjDir == "right")
277 {
278 mappable.rightAdjacent = {adjType, adjMapId};
279 } else if (adjDir == "up")
280 {
281 mappable.upAdjacent = {adjType, adjMapId};
282 } else if (adjDir == "down")
283 {
284 mappable.downAdjacent = {adjType, adjMapId};
285 } else {
286 throw std::logic_error("Invalid adjacency direction");
287 }
288 }
289 }
290
291 mapping.generateBoundaries(map);
292
293 entityByMapId_[mappable.mapId] = map;
294 }
295 }
296
297 xmlFreeDoc(doc);
298 xmlFreeDoc(protoXml);
299
300 activateMap(entityByMapId_[startingMapId_]);
301}
302
303void RealizingSystem::loadMap(id_type mapEntity)
304{
305 deactivateMap();
306 activateMap(mapEntity);
307}
308
309void RealizingSystem::deactivateMap()
310{
311 id_type oldMap = activeMap_;
312
313 auto& oldMappable = game_.getEntityManager().
314 getComponent<MappableComponent>(oldMap);
315
316 // Deactivate any map objects from the old map.
317 for (id_type prototype : oldMappable.objects)
318 {
319 leaveActiveMap(prototype);
320 }
321
322 // Deactivate players that were on the old map.
323 std::set<id_type> players =
324 game_.getEntityManager().getEntitiesWithComponents<
325 PlayableComponent>();
326
327 for (id_type player : players)
328 {
329 auto& playable = game_.getEntityManager().
330 getComponent<PlayableComponent>(player);
331
332 if (playable.mapId == oldMap)
333 {
334 leaveActiveMap(player);
335 }
336 }
337}
338
339void RealizingSystem::activateMap(id_type mapEntity)
340{
341 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
342 auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>();
343
344 std::set<id_type> players =
345 game_.getEntityManager().getEntitiesWithComponents<
346 PlayableComponent>();
347
348 activeMap_ = mapEntity;
349
350 auto& mappable = game_.getEntityManager().
351 getComponent<MappableComponent>(mapEntity);
352
353 // Initialize the new map's objects.
354 for (id_type prototype : mappable.objects)
355 {
356 if (game_.getEntityManager().
357 hasComponent<TransformableComponent>(prototype))
358 {
359 auto& transformable = game_.getEntityManager().
360 getComponent<TransformableComponent>(prototype);
361
362 transformable.pos = transformable.origPos;
363 transformable.size = transformable.origSize;
364 }
365
366 if (game_.getEntityManager().hasComponent<AnimatableComponent>(prototype))
367 {
368 animating.initPrototype(prototype);
369 }
370
371 if (game_.getEntityManager().hasComponent<PonderableComponent>(prototype))
372 {
373 pondering.initPrototype(prototype);
374 }
375
376 enterActiveMap(prototype);
377 }
378
379 // Activate any players on the map.
380 for (id_type player : players)
381 {
382 auto& playable = game_.getEntityManager().
383 getComponent<PlayableComponent>(player);
384
385 if (playable.mapId == mapEntity)
386 {
387 enterActiveMap(player);
388 }
389 }
390}
391
392void RealizingSystem::enterActiveMap(id_type entity)
393{
394 if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity))
395 {
396 auto& animatable = game_.getEntityManager().
397 getComponent<AnimatableComponent>(entity);
398
399 animatable.active = true;
400 }
401
402 if (game_.getEntityManager().hasComponent<PonderableComponent>(entity))
403 {
404 auto& ponderable = game_.getEntityManager().
405 getComponent<PonderableComponent>(entity);
406
407 ponderable.active = true;
408 }
409
410 if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity))
411 {
412 game_.getSystemManager().getSystem<ScriptingSystem>().startBehavior(entity);
413 }
414}
415
416void RealizingSystem::leaveActiveMap(id_type entity)
417{
418 if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity))
419 {
420 auto& animatable = game_.getEntityManager().
421 getComponent<AnimatableComponent>(entity);
422
423 animatable.active = false;
424 }
425
426 if (game_.getEntityManager().hasComponent<PonderableComponent>(entity))
427 {
428 auto& ponderable = game_.getEntityManager().
429 getComponent<PonderableComponent>(entity);
430
431 ponderable.active = false;
432 }
433
434 if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity))
435 {
436 game_.getSystemManager().getSystem<ScriptingSystem>().stopBehavior(entity);
437 }
438}
diff --git a/src/systems/realizing.h b/src/systems/realizing.h new file mode 100644 index 0000000..ab5a150 --- /dev/null +++ b/src/systems/realizing.h
@@ -0,0 +1,86 @@
1#ifndef REALIZING_H_6853748C
2#define REALIZING_H_6853748C
3
4#include <string>
5#include <map>
6#include "system.h"
7#include "vector.h"
8
9class RealizingSystem : public System {
10public:
11
12 /**
13 * Constructs the realizing system.
14 *
15 * Note that this must be constructed after the following system:
16 * - Mapping
17 * - Animating
18 * - Pondering
19 * - Scripting
20 */
21 RealizingSystem(
22 Game& game,
23 std::string worldFile,
24 std::string prototypeFile);
25
26 id_type getActiveMap() const
27 {
28 return activeMap_;
29 }
30
31 int getStartingMapId() const
32 {
33 return startingMapId_;
34 }
35
36 vec2i getStartingPos() const
37 {
38 return startingPos_;
39 }
40
41 id_type getEntityByMapId(size_t mapId) const
42 {
43 return entityByMapId_.at(mapId);
44 }
45
46 id_type getActivePlayer() const
47 {
48 return activePlayer_;
49 }
50
51 void setActivePlayer(id_type entity)
52 {
53 activePlayer_ = entity;
54 }
55
56 /**
57 * Loads the given map.
58 */
59 void loadMap(id_type mapEntity);
60
61 /**
62 * Treats the given entity as part of the active map.
63 */
64 void enterActiveMap(id_type entity);
65
66 /**
67 * Stops treating the given entity as part of the active map.
68 */
69 void leaveActiveMap(id_type entity);
70
71private:
72
73 void deactivateMap();
74
75 void activateMap(id_type mapEntity);
76
77 std::string worldFile_;
78 std::string prototypeFile_;
79 int startingMapId_;
80 vec2i startingPos_;
81 std::map<size_t, id_type> entityByMapId_;
82 id_type activeMap_;
83 id_type activePlayer_;
84};
85
86#endif /* end of include guard: REALIZING_H_6853748C */
diff --git a/src/systems/scheduling.cpp b/src/systems/scheduling.cpp new file mode 100644 index 0000000..220efae --- /dev/null +++ b/src/systems/scheduling.cpp
@@ -0,0 +1,54 @@
1#include "scheduling.h"
2#include "game.h"
3#include "components/schedulable.h"
4#include "util.h"
5
6void SchedulingSystem::tick(double dt)
7{
8 auto entities = game_.getEntityManager().getEntitiesWithComponents<
9 SchedulableComponent>();
10
11 for (id_type entity : entities)
12 {
13 auto& schedulable = game_.getEntityManager().
14 getComponent<SchedulableComponent>(entity);
15
16 for (auto& action : schedulable.actions)
17 {
18 std::get<0>(action) -= dt;
19
20 if (std::get<0>(action) < 0)
21 {
22 std::get<1>(action)(entity);
23 }
24 }
25
26 erase_if(schedulable.actions,
27 [] (const SchedulableComponent::Action& action) {
28 return (std::get<0>(action) < 0);
29 });
30
31 if (schedulable.actions.empty())
32 {
33 game_.getEntityManager().removeComponent<SchedulableComponent>(entity);
34 }
35 }
36}
37
38void SchedulingSystem::schedule(
39 id_type entity,
40 double length,
41 std::function<void(id_type)> action)
42{
43 if (!game_.getEntityManager().hasComponent<SchedulableComponent>(entity))
44 {
45 game_.getEntityManager().emplaceComponent<SchedulableComponent>(entity);
46 }
47
48 auto& schedulable = game_.getEntityManager().
49 getComponent<SchedulableComponent>(entity);
50
51 schedulable.actions.emplace_back(
52 length,
53 std::move(action));
54}
diff --git a/src/systems/scheduling.h b/src/systems/scheduling.h new file mode 100644 index 0000000..7950d62 --- /dev/null +++ b/src/systems/scheduling.h
@@ -0,0 +1,22 @@
1#ifndef SCHEDULING_H_7B02E3E3
2#define SCHEDULING_H_7B02E3E3
3
4#include "system.h"
5
6class SchedulingSystem : public System {
7public:
8
9 SchedulingSystem(Game& game) : System(game)
10 {
11 }
12
13 void tick(double dt);
14
15 void schedule(
16 id_type entity,
17 double length,
18 std::function<void(id_type)> action);
19
20};
21
22#endif /* end of include guard: SCHEDULING_H_7B02E3E3 */
diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp new file mode 100644 index 0000000..504224c --- /dev/null +++ b/src/systems/scripting.cpp
@@ -0,0 +1,257 @@
1#include "scripting.h"
2#include "game.h"
3#include "components/runnable.h"
4#include "components/ponderable.h"
5#include "components/transformable.h"
6#include "components/playable.h"
7#include "components/mappable.h"
8#include "components/prototypable.h"
9#include "components/automatable.h"
10#include "systems/realizing.h"
11#include "vector.h"
12#include "muxer.h"
13
14struct script_entity {
15 using id_type = EntityManager::id_type;
16
17 id_type id;
18
19 script_entity(id_type id) : id(id)
20 {
21 }
22};
23
24ScriptingSystem::ScriptingSystem(Game& game) : System(game)
25{
26 id_type entity = game_.getEntityManager().emplaceEntity();
27
28 engine_.open_libraries(sol::lib::base, sol::lib::coroutine);
29
30 engine_.new_usertype<vec2d>(
31 "vec2d",
32 sol::constructors<vec2d(), vec2d(double, double)>(),
33 "x", sol::property(
34 [] (vec2d& v) -> double { return v.x(); },
35 [] (vec2d& v, double x) { v.x() = x; }),
36 "y", sol::property(
37 [] (vec2d& v) -> double { return v.y(); },
38 [] (vec2d& v, double y) { v.y() = y; }));
39
40 engine_.new_usertype<vec2i>(
41 "vec2i",
42 sol::constructors<vec2i(), vec2i(int, int)>(),
43 "x", [] (vec2i& v) -> int& { return v.x(); },
44 "y", [] (vec2i& v) -> int& { return v.y(); });
45
46 engine_.new_usertype<script_entity>(
47 "entity",
48 sol::constructors<script_entity(id_type)>(),
49 "id", &script_entity::id,
50 "transformable",
51 [&] (script_entity& entity) -> TransformableComponent& {
52 return game_.getEntityManager().
53 getComponent<TransformableComponent>(entity.id);
54 },
55 "ponderable",
56 [&] (script_entity& entity) -> PonderableComponent& {
57 return game_.getEntityManager().
58 getComponent<PonderableComponent>(entity.id);
59 },
60 "mappable",
61 [&] (script_entity& entity) -> MappableComponent& {
62 return game_.getEntityManager().
63 getComponent<MappableComponent>(entity.id);
64 },
65 "playable",
66 [&] (script_entity& entity) -> PlayableComponent& {
67 return game_.getEntityManager().
68 getComponent<PlayableComponent>(entity.id);
69 },
70 "prototypable",
71 [&] (script_entity& entity) -> PrototypableComponent& {
72 return game_.getEntityManager().
73 getComponent<PrototypableComponent>(entity.id);
74 });
75
76 engine_.new_usertype<TransformableComponent>(
77 "transformable",
78 "pos", &TransformableComponent::pos);
79
80 engine_.new_usertype<PonderableComponent>(
81 "ponderable",
82 "vel", &PonderableComponent::vel,
83 "accel", &PonderableComponent::accel);
84
85 engine_.new_usertype<MappableComponent>(
86 "mappable",
87 "mapId", &MappableComponent::mapId);
88
89 engine_.new_usertype<PlayableComponent>(
90 "playable",
91 "checkpointPos", &PlayableComponent::checkpointPos,
92 "checkpointMapId", &PlayableComponent::checkpointMapId,
93 "checkpointMapObject", &PlayableComponent::checkpointMapObject,
94 "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex);
95
96 engine_.new_usertype<PrototypableComponent>(
97 "prototypable",
98 "mapObjectIndex", &PrototypableComponent::mapObjectIndex,
99 "prototypeId", &PrototypableComponent::prototypeId);
100
101 engine_.new_usertype<RealizingSystem>(
102 "realizing",
103 "activeMap", sol::property(&RealizingSystem::getActiveMap));
104
105 engine_.set_function(
106 "realizing",
107 [&] () -> RealizingSystem& {
108 return game_.getSystemManager().getSystem<RealizingSystem>();
109 });
110
111 engine_.set_function("playSound", playSound);
112
113 engine_.script_file("scripts/common.lua");
114 engine_.script_file("scripts/movplat.lua");
115 engine_.script_file("scripts/checkpoint.lua");
116}
117
118void ScriptingSystem::tick(double dt)
119{
120 auto entities = game_.getEntityManager().getEntitiesWithComponents<
121 RunnableComponent>();
122
123 for (id_type entity : entities)
124 {
125 auto& runnable = game_.getEntityManager().
126 getComponent<RunnableComponent>(entity);
127
128 if (*runnable.callable)
129 {
130 auto result = (*runnable.callable)(dt);
131 if (!result.valid())
132 {
133 sol::error e = result;
134 throw std::runtime_error(e.what());
135 }
136 }
137
138 if (!*runnable.callable)
139 {
140 killScript(entity);
141 }
142 }
143}
144
145void ScriptingSystem::killScript(id_type entity)
146{
147 auto& runnable = game_.getEntityManager().
148 getComponent<RunnableComponent>(entity);
149
150 if (runnable.behavior)
151 {
152 auto& automatable = game_.getEntityManager().
153 getComponent<AutomatableComponent>(runnable.actor);
154
155 automatable.running = false;
156 }
157
158 game_.getEntityManager().deleteEntity(entity);
159}
160
161template <typename... Args>
162sol::optional<EntityManager::id_type> ScriptingSystem::runScript(
163 std::string table,
164 std::string event,
165 id_type entity,
166 Args&&... args)
167{
168 id_type script = game_.getEntityManager().emplaceEntity();
169
170 auto& runnable = game_.getEntityManager().
171 emplaceComponent<RunnableComponent>(script);
172
173 runnable.runner =
174 std::unique_ptr<sol::thread>(
175 new sol::thread(
176 sol::thread::create(
177 engine_.lua_state())));
178
179 runnable.callable =
180 std::unique_ptr<sol::coroutine>(
181 new sol::coroutine(
182 runnable.runner->state().
183 traverse_get<sol::function>(
184 table,
185 event)));
186
187 if (!*runnable.callable)
188 {
189 throw std::runtime_error("Error running script");
190 }
191
192 auto result = (*runnable.callable)(
193 script_entity(entity),
194 std::forward<Args>(args)...);
195
196 if (!result.valid())
197 {
198 sol::error e = result;
199 throw std::runtime_error(e.what());
200 }
201
202 if (*runnable.callable)
203 {
204 return { script };
205 } else {
206 killScript(script);
207
208 return {};
209 }
210}
211
212void ScriptingSystem::startBehavior(id_type entity)
213{
214 auto& automatable = game_.getEntityManager().
215 getComponent<AutomatableComponent>(entity);
216
217 sol::optional<id_type> script =
218 runScript(
219 automatable.table,
220 "Behavior",
221 entity);
222
223 if (script)
224 {
225 automatable.script = *script;
226 automatable.running = true;
227
228 auto& runnable = game_.getEntityManager().
229 getComponent<RunnableComponent>(automatable.script);
230
231 runnable.behavior = true;
232 runnable.actor = entity;
233 }
234}
235
236void ScriptingSystem::stopBehavior(id_type entity)
237{
238 auto& automatable = game_.getEntityManager().
239 getComponent<AutomatableComponent>(entity);
240
241 if (automatable.running)
242 {
243 killScript(automatable.script);
244 }
245}
246
247void ScriptingSystem::onTouch(id_type entity, id_type player)
248{
249 auto& prototypable = game_.getEntityManager().
250 getComponent<PrototypableComponent>(entity);
251
252 runScript(
253 prototypable.prototypeId,
254 "OnTouch",
255 entity,
256 script_entity(player));
257}
diff --git a/src/systems/scripting.h b/src/systems/scripting.h new file mode 100644 index 0000000..b119c3f --- /dev/null +++ b/src/systems/scripting.h
@@ -0,0 +1,34 @@
1#ifndef AUTOMATING_H_E6E5D76E
2#define AUTOMATING_H_E6E5D76E
3
4#include "system.h"
5#include <sol.hpp>
6
7class ScriptingSystem : public System {
8public:
9
10 ScriptingSystem(Game& game);
11
12 void tick(double dt);
13
14 void killScript(id_type entity);
15
16 void startBehavior(id_type entity);
17
18 void stopBehavior(id_type entity);
19
20 void onTouch(id_type entity, id_type player);
21
22private:
23
24 template <typename... Args>
25 sol::optional<id_type> runScript(
26 std::string table,
27 std::string event,
28 id_type entity,
29 Args&&... args);
30
31 sol::state engine_;
32};
33
34#endif /* end of include guard: AUTOMATING_H_E6E5D76E */
diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..f0c39fd --- /dev/null +++ b/src/util.cpp
@@ -0,0 +1,30 @@
1#include "util.h"
2
3std::string slurp(std::ifstream& in)
4{
5 std::stringstream sstr;
6 sstr << in.rdbuf();
7 return sstr.str();
8}
9
10void flipImageData(
11 unsigned char* data,
12 int width,
13 int height,
14 int comps)
15{
16 unsigned char* dataCopy = new unsigned char[width * height * comps];
17 memcpy(dataCopy, data, width * height * comps);
18
19 int rowSize = width * comps;
20
21 for (int i = 0; i < height; i++)
22 {
23 memcpy(
24 data + (rowSize * i),
25 dataCopy + (rowSize * (height - i - 1)),
26 rowSize);
27 }
28
29 delete[] dataCopy;
30}
diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..72cb0d3 --- /dev/null +++ b/src/util.h
@@ -0,0 +1,41 @@
1#ifndef ALGORITHMS_H_1DDC517E
2#define ALGORITHMS_H_1DDC517E
3
4#include <fstream>
5#include <sstream>
6#include <cstring>
7
8template< typename ContainerT, typename PredicateT >
9void erase_if( ContainerT& items, const PredicateT& predicate ) {
10 for( auto it = items.begin(); it != items.end(); ) {
11 if( predicate(*it) ) it = items.erase(it);
12 else ++it;
13 }
14};
15
16struct chlit
17{
18 chlit(char c) : c_(c) { }
19 char c_;
20};
21
22inline std::istream& operator>>(std::istream& is, chlit x)
23{
24 char c;
25 if (is >> c && c != x.c_)
26 {
27 is.setstate(std::iostream::failbit);
28 }
29
30 return is;
31}
32
33std::string slurp(std::ifstream& in);
34
35void flipImageData(
36 unsigned char* data,
37 int width,
38 int height,
39 int comps);
40
41#endif /* end of include guard: ALGORITHMS_H_1DDC517E */
diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..9355dd5 --- /dev/null +++ b/src/vector.h
@@ -0,0 +1,113 @@
1#ifndef COORDINATES_H_A45D34FB
2#define COORDINATES_H_A45D34FB
3
4template <typename T>
5class vec2 {
6public:
7
8 T coords[2];
9
10 vec2() : coords{0, 0}
11 {
12 }
13
14 vec2(T x, T y) : coords{x, y}
15 {
16 }
17
18 inline T& x()
19 {
20 return coords[0];
21 }
22
23 inline const T& x() const
24 {
25 return coords[0];
26 }
27
28 inline T& w()
29 {
30 return coords[0];
31 }
32
33 inline const T& w() const
34 {
35 return coords[0];
36 }
37
38 inline T& y()
39 {
40 return coords[1];
41 }
42
43 inline const T& y() const
44 {
45 return coords[1];
46 }
47
48 inline T& h()
49 {
50 return coords[1];
51 }
52
53 inline const T& h() const
54 {
55 return coords[1];
56 }
57
58 template <typename R>
59 operator vec2<R>() const
60 {
61 return vec2<R>(x(), y());
62 }
63
64 vec2 operator+(const vec2& other) const
65 {
66 return vec2(x() + other.x(), y() + other.y());
67 }
68
69 vec2& operator+=(const vec2& other)
70 {
71 x() += other.x();
72 y() += other.y();
73
74 return *this;
75 }
76
77 vec2 operator-(const vec2& other) const
78 {
79 return vec2(x() - other.x(), y() - other.y());
80 }
81
82 vec2 operator-=(const vec2& other)
83 {
84 x() -= other.x();
85 y() -= other.y();
86
87 return *this;
88 }
89
90 vec2 operator-() const
91 {
92 return vec2(-x(), -y());
93 }
94
95 vec2 operator*(T s) const
96 {
97 return vec2(x() * s, y() * s);
98 }
99
100 vec2& operator*=(T s)
101 {
102 x() *= s;
103 y() *= s;
104
105 return *this;
106 }
107
108};
109
110using vec2d = vec2<double>;
111using vec2i = vec2<int>;
112
113#endif /* end of include guard: COORDINATES_H_A45D34FB */
diff --git a/src/world.cpp b/src/world.cpp deleted file mode 100644 index 0c61c47..0000000 --- a/src/world.cpp +++ /dev/null
@@ -1,150 +0,0 @@
1#include "world.h"
2#include <libxml/parser.h>
3#include "consts.h"
4
5World::World(const char* filename)
6{
7 xmlDocPtr doc = xmlParseFile(filename);
8 if (doc == nullptr)
9 {
10 exit(2);
11 }
12
13 xmlNodePtr top = xmlDocGetRootElement(doc);
14 if (top == nullptr)
15 {
16 exit(2);
17 }
18
19 if (xmlStrcmp(top->name, (const xmlChar*) "world"))
20 {
21 exit(2);
22 }
23
24 xmlChar* startxKey = xmlGetProp(top, (xmlChar*) "startx");
25 if (startxKey == 0) exit(2);
26 startX = atoi((char*) startxKey);
27 xmlFree(startxKey);
28
29 xmlChar* startyKey = xmlGetProp(top, (xmlChar*) "starty");
30 if (startyKey == 0) exit(2);
31 startY = atoi((char*) startyKey);
32 xmlFree(startyKey);
33
34 xmlChar* startmapKey = xmlGetProp(top, (xmlChar*) "startmap");
35 if (startxKey == 0) exit(2);
36 startMap = atoi((char*) startmapKey);
37 xmlFree(startmapKey);
38
39 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
40 {
41 if (!xmlStrcmp(node->name, (const xmlChar*) "map"))
42 {
43 xmlChar* idKey = xmlGetProp(node, (xmlChar*) "id");
44 if (idKey == 0) exit(2);
45 int theId = atoi((char*) idKey);
46 xmlFree(idKey);
47
48 maps.emplace(theId, theId);
49 Map& map = maps[theId];
50
51 xmlChar* titleKey = xmlGetProp(node, (xmlChar*) "title");
52 if (titleKey == 0) exit(2);
53 map.setTitle((char*) titleKey);
54 xmlFree(titleKey);
55
56 for (xmlNodePtr mapNode = node->xmlChildrenNode; mapNode != NULL; mapNode = mapNode->next)
57 {
58 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment"))
59 {
60 xmlChar* key = xmlNodeGetContent(mapNode);
61 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
62 mapdata[0] = atoi(strtok((char*) key, ",\n"));
63 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++)
64 {
65 mapdata[i] = atoi(strtok(NULL, ",\n"));
66 }
67 map.setMapdata(mapdata);
68 xmlFree(key);
69 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "entity"))
70 {
71 Map::EntityData data;
72
73 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type");
74 if (typeKey == 0) exit(2);
75 data.name = (char*) typeKey;
76 xmlFree(typeKey);
77
78 xmlChar* xKey = xmlGetProp(mapNode, (const xmlChar*) "x");
79 if (xKey == 0) exit(2);
80 data.position.first = atoi((char*) xKey);
81 xmlFree(xKey);
82
83 xmlChar* yKey = xmlGetProp(mapNode, (const xmlChar*) "y");
84 if (yKey == 0) exit(2);
85 data.position.second = atoi((char*) yKey);
86 xmlFree(yKey);
87
88 for (xmlNodePtr entityNode = mapNode->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
89 {
90 if (!xmlStrcmp(entityNode->name, (xmlChar*) "item"))
91 {
92 xmlChar* itemIdKey = xmlGetProp(entityNode, (const xmlChar*) "id");
93 if (itemIdKey == 0) exit(2);
94 std::string itemId = (char*) itemIdKey;
95 xmlFree(itemIdKey);
96
97 xmlChar* itemIdVal = xmlNodeGetContent(entityNode);
98 if (itemIdVal == 0) exit(2);
99 data.items[itemId] = atoi((char*) itemIdVal);
100 xmlFree(itemIdVal);
101 }
102 }
103
104 map.addEntity(data);
105 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent"))
106 {
107 Map::MoveDir direction;
108 Map::MoveType moveType;
109 int mapId = 0;
110
111 xmlChar* dirKey = xmlGetProp(mapNode, (const xmlChar*) "dir");
112 if (dirKey == 0) exit(2);
113 direction = Map::moveDirForShort((char*) dirKey);
114 xmlFree(dirKey);
115
116 xmlChar* typeKey = xmlGetProp(mapNode, (const xmlChar*) "type");
117 if (typeKey == 0) exit(2);
118 moveType = Map::moveTypeForShort((char*) typeKey);
119 xmlFree(typeKey);
120
121 xmlChar* mapIdKey = xmlGetProp(mapNode, (const xmlChar*) "map");
122 if (mapIdKey != 0)
123 {
124 mapId = atoi((char*) mapIdKey);
125 }
126 xmlFree(mapIdKey);
127
128 map.setAdjacent(direction, moveType, mapId);
129 }
130 }
131 }
132 }
133
134 xmlFreeDoc(doc);
135}
136
137const Map& World::getMap(int id) const
138{
139 return maps.at(id);
140}
141
142const Map& World::getStartingMap() const
143{
144 return maps.at(startMap);
145}
146
147std::pair<int, int> World::getStartingPosition() const
148{
149 return std::make_pair(startX, startY);
150}
diff --git a/src/world.h b/src/world.h deleted file mode 100644 index f566487..0000000 --- a/src/world.h +++ /dev/null
@@ -1,21 +0,0 @@
1#ifndef WORLD_H
2#define WORLD_H
3
4#include <map>
5#include "map.h"
6
7class World {
8 public:
9 World(const char* filename);
10 const Map& getMap(int id) const;
11 const Map& getStartingMap() const;
12 std::pair<int, int> getStartingPosition() const;
13
14 private:
15 std::map<int, Map> maps;
16 int startMap;
17 int startX;
18 int startY;
19};
20
21#endif /* end of include guard: WORLD_H */