08-25-2019, 07:50 AM
Flipping Images
Vertical and Horizontal Flips
Vertical and Horizontal Flips
I wanted to share some C code with you guys in order to let you learn a bit about low level languages and how they handle image flipping internally. You wouldn't imagine that it could be that simple in the first place because some programming languages posses features others don't.
Let's take a look at two well known languages, C and Ruby. Actually Ruby is a C backed Object Oriented Programming language with other paradigms included as well. Still, anybody that has dealt with Ruby scripts or even RGSS ones would realize how many things seem to be darn easy for anybody to learn. Sometimes I wonder why most people never touch a script unless its one of the worst like a battle system. It's definitely not the best script to start with. Things like something as useful as Ruby's Range class should be a must for any language. Others like Java seem to include that feature already. That's not C's case nonetheless.
Ruby Range Class
@array[0..3]
Yeah, it's just a number followed by a few dots that later ends with another number. Since version 2.6 you can skip the last argument if you feel like it. And people don't believe me Ruby is ideal for lazy people.
C Range Function!? I'm sorry there's no such a thing. Really! It has lived for decades and it still doesn't offer us anything like that. Programmers' advice consists of telling you to implement a loop, especially in a helper function.
Code:
typedef stbi_uc unsigned char;
Code:
// Horizontal Flip by Kyonides Arkanthes shared under GPLv2 or v3
static void stbi_kyon_horizontal_flip(void *image, int w, int h, int bytes_per_pixel)
{
size_t line_bytes = (size_t)w * bytes_per_pixel;
stbi_uc temp[line_bytes];
stbi_uc *bytes = (stbi_uc *)image;
int lpos, rpos;
for (int col = 0; col < h; col++) {
stbi_uc *line = bytes + col * line_bytes;
memcpy(&temp, line, line_bytes);
for (int row = 0; row < w; row++) {
lpos = row * bytes_per_pixel;
rpos = line_bytes - row * bytes_per_pixel - 1;
line[lpos] = temp[rpos - 3];
line[lpos + 1] = temp[rpos - 2];
line[lpos + 2] = temp[rpos - 1];
line[lpos + 3] = temp[rpos];
}
}
stbi_kyon_horizontally_flip_on_load = false;
}
I know the code above works because I've included it in my A Short Odyssey Gosu project already.
Code:
static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
{
int row;
size_t bytes_per_row = (size_t)w * bytes_per_pixel;
stbi_uc temp[2048];
stbi_uc *bytes = (stbi_uc *)image;
for (row = 0; row < (h>>1); row++) {
stbi_uc *row0 = bytes + row * bytes_per_row;
stbi_uc *row1 = bytes + (h - row - 1) * bytes_per_row;
// swap row0 with row1
size_t bytes_left = bytes_per_row;
while (bytes_left) {
size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
memcpy(temp, row0, bytes_copy);
memcpy(row0, row1, bytes_copy);
memcpy(row1, temp, bytes_copy);
row0 += bytes_copy;
row1 += bytes_copy;
bytes_left -= bytes_copy;
}
}
}
Why did I share my code and stb_image.h's? Well, it's because it shows you what you can do when you don't count on ranges to save the day. It looks ugly, isn't it?
As you might ignore C arrays are weird. You don't look for its first position like in Ruby with something like array[0] but you just type array. Strange indeed! That's not the end of this spooky tale, guys! In order to tell C that you want some index you've got two ways to do that.
Option 1
array[offset + 1]
Option 2
array + offset + 1
What the hell is that supposed to be!? Well, in C you can "add" a position as if array were equal to zero because arrays are pointers and they always reference its first position anyway. And it gets weirder and weirder if you include nested arrays...
Keep in mind that people usually don't pass stuff like C arrays by value but as a pointer or reference. It means you pass a memory direction to the array you've created. It's weird to do the same with other stuff like numbers either integers like 8 or floats like 1.3 also known as double if you require double precision in float computation. You could end up passing a different value, its actual address that might look like 34651468167. Oh and there's no guarantee it will always be there. Try to place a pixel in such a position and you'll end up with missing pixels letting you think you've got a broken monitor of sorts.
By the way, don't abuse of the memcpy function that allows you to duplicate any variable's contents and store them in a destination variable. Programmers kind of fear it since it might still come up with some unexpected behavior.
What's the benefit of passing variables by reference? Err, well, you can alter them in a different function without needing to return it at the end of its execution, even if nothing forbids you of doing so if you wanna.
In C and C++ you'll find people setting and getting a variable's value via setter and getter functions. Setters usually are void since you're supposed to know what kind of value you're passing them from the very beginning. void means no return value. In this case it's like returning a nil in Ruby except nil is still some value or object for Ruby! Actually a simple return with no argument in Ruby is identical to typing return nil. In C or C++ you make a function not return a thing by typing return; yeah with semicolon included. Fail to add it and the compiler will start screaming at you! Ruby is really nice indeed!
What's the typedef? Err, that's a reserved keyword that tells either C or C++ that your code defines a fictitious type of variable or function named after the first argument that represents the other arguments. It's shorthand for whatever you don't wanna keep typing the whole day. Yeah, it's true! Even in C or C++ you can be a bit lazy as well.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE