Skip to content

Commit

Permalink
Added support for GIF
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Aug 20, 2015
1 parent 60e8a7c commit 3ed2e75
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 48 deletions.
79 changes: 60 additions & 19 deletions src/catimg.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "sh_image.h"
#include "sh_utils.h"
#include <unistd.h>
#include <signal.h>
#define USAGE printf("Usage catimg [-h] [-w width] img\nBy default w is the terminal width.\n")

extern char *optarg;
Expand All @@ -11,6 +12,15 @@ extern int optopt;
extern int opterr;
extern int optreset;

// For <C-C>
volatile int loops = -1, loop = -1;
volatile char stop = 0;

void intHandler() {
loops = loop;
stop = 1;
}

int getopt(int argc, char * const argv[], const char *optstring);

int main(int argc, char *argv[])
Expand Down Expand Up @@ -51,28 +61,59 @@ int main(int argc, char *argv[])
}
img_convert_colors(&img);
/*printf("Loaded %s: %ux%u. Console width: %u\n", file, img.width, img.height, cols);*/
uint32_t w = 0;
for (uint32_t i = 0; i < img.width*img.height; i++) {
if (img.pixels[i].a == 0) {
printf("\e[m ");
} else {
uint32_t col;
if (img.pixels[i].r == img.pixels[i].g && img.pixels[i].g == img.pixels[i].b)
col = 232 + (img.pixels[i].r*23)/255;
else
col = (16 + ((img.pixels[i].r*5)/255)*36
+ ((img.pixels[i].g*5)/255)*6
+ (img.pixels[i].b*5)/255);
// For GIF
if (img.frames > 1) {
system("clear");
signal(SIGINT, intHandler);
} else {
loops = 0;
}
// Save the cursor position and hide it
printf("\e[s\e[?25l");
while (loop++ < loops || loops < 0) {
uint32_t w = 0, offset = 0;
for (uint32_t frame = 0; frame < img.frames; frame++) {
if (frame > 0 || loop > 0) {
if (frame > 0)
usleep(img.delays[frame - 1] * 10000);
else
usleep(img.delays[img.frames - 1] * 10000);
printf("\e[u");
}
uint32_t i, index;
for (i = 0; i < img.width*img.height; i++) {
index = i + offset;
if (img.pixels[index].a == 0) {
printf("\e[m ");
} else {
uint32_t col;
if (img.pixels[index].r == img.pixels[index].g && img.pixels[index].g == img.pixels[index].b)
col = 232 + (img.pixels[index].r*23)/255;
else
col = (16 + ((img.pixels[index].r*5)/255)*36
+ ((img.pixels[index].g*5)/255)*6
+ (img.pixels[index].b*5)/255);

/*printf("(%u,%u,%u)", img.pixels[i].r, img.pixels[i].g, img.pixels[i].b);*/
printf("\e[48;5;%um ", col);
}
w++;
if (w >= img.width) {
w = 0;
printf("\e[m\n");
/*printf("(%u,%u,%u)", img.pixels[index].r, img.pixels[index].g, img.pixels[index].b);*/
printf("\e[48;5;%um ", col);
}
w++;
if (w >= img.width) {
w = 0;
printf("\e[m\n");
}
}
offset += i;
if (w != 0 && w < img.width) {
w = 0;
printf("\e[m\n");
}
if (stop) frame = img.frames;
}
}
// Display the cursor again
printf("\e[?25h");

img_free(&img);
free_hash_colors();
return 0;
Expand Down
203 changes: 176 additions & 27 deletions src/sh_image.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,169 @@
#include <stdlib.h>
#include <assert.h>

typedef struct gif_result_t {
int delay;
unsigned char *data;
struct gif_result_t *next;
} gif_result;

// stbi_xload was written by urraka and slighty modified by me (posva) to make
// it have a channels argument. Thanks to urraka for his help with this :)
STBIDEF unsigned char *stbi_xload(char const *filename, int *x, int *y, int *frames, int *channels)
{
FILE *f;
stbi__context s;
unsigned char *result = 0;

if (!(f = stbi__fopen(filename, "rb")))
return stbi__errpuc("can't fopen", "Unable to open file");

stbi__start_file(&s, f);

if (stbi__gif_test(&s))
{
stbi__gif g;
gif_result head;
gif_result *prev = 0, *gr = &head;

memset(&g, 0, sizeof(g));
memset(&head, 0, sizeof(head));

*frames = 0;

while (gr->data = stbi__gif_load_next(&s, &g, channels, 4))
{
if (gr->data == (unsigned char*)&s)
{
gr->data = 0;
break;
}

if (prev) prev->next = gr;
gr->delay = g.delay;
prev = gr;
gr = (gif_result*) stbi__malloc(sizeof(gif_result));
memset(gr, 0, sizeof(gif_result));
++(*frames);
}

if (gr != &head)
STBI_FREE(gr);

if (*frames > 0)
{
*x = g.w;
*y = g.h;
}

result = head.data;

if (*frames > 1)
{
unsigned int size = 4 * g.w * g.h;
unsigned char *p = 0;

result = (unsigned char*)stbi__malloc(*frames * (size + 2));
gr = &head;
p = result;

while (gr)
{
prev = gr;
memcpy(p, gr->data, size);
p += size;
*p++ = gr->delay & 0xFF;
*p++ = (gr->delay & 0xFF00) >> 8;
gr = gr->next;

STBI_FREE(prev->data);
if (prev != &head) STBI_FREE(prev);
}
}
}
else
{
result = stbi__load_main(&s, x, y, channels, 0);
*frames = !!result;
}

fclose(f);
return result;
}

void setPixelGray(color_t *pixel, unsigned char* ptr) {
pixel->r = pixel->g = pixel->b = ptr[0];
pixel->a = 255;
}

void setPixelGrayAlpha(color_t *pixel, unsigned char* ptr) {
pixel->r = pixel->g = pixel->b = ptr[0];
pixel->a = ptr[1];
}

void setPixelRGB(color_t *pixel, unsigned char* ptr) {
pixel->r = ptr[0];
pixel->g = ptr[1];
pixel->b = ptr[2];
pixel->a = 255;
}

void setPixelRGBAlpha(color_t *pixel, unsigned char* ptr) {
pixel->r = ptr[0];
pixel->g = ptr[1];
pixel->b = ptr[2];
pixel->a = ptr[3];
}

void img_load_from_file(image_t *img, const char* file)
{
int channels, w, h;
unsigned char* ptr = stbi_load(file, &w, &h, &channels, STBI_rgb_alpha);
int channels, w, h, frames;
/*unsigned char* ptr = stbi_load(file, &w, &h, &channels, 0);*/
unsigned char* ptr = stbi_xload(file, &w, &h, &frames, &channels);

if (ptr && w && h) {
img->width = w;
/*h *= 3;*/
img->height = h;
img->delays = NULL;
img->frames = frames;

if (!(img->pixels = malloc(sizeof(color_t)*w*h))) {
if (!(img->pixels = malloc(sizeof(color_t)*w*h * frames))) {
perror("malloc error\n");
exit(1);
}

if (frames > 1 && !(img->delays = malloc(sizeof(uint16_t) * (frames - 1)))) {
perror("malloc error\n");
exit(1);
}

// fill the array
for (int i = 0, j = 0; j < w*h; i += 4, j++) {
img->pixels[j].r = ptr[i];
img->pixels[j].g = ptr[i+1];
img->pixels[j].b = ptr[i+2];
img->pixels[j].a = ptr[i+3];
void (*pixelSetter)(color_t *pixel, unsigned char* ptr);
switch (channels) {
case 1:
pixelSetter = &setPixelGray;
break;
case 2:
pixelSetter = &setPixelGrayAlpha;
break;
case 3:
pixelSetter = &setPixelRGB;
break;
case 4:
pixelSetter = &setPixelRGBAlpha;
break;
}
for (int frame = 0; frame < frames; frame++) {
int offset = frame * (sizeof(unsigned char) * channels * w*h + 2);
int i = 0;
for (int j = 0; j < w*h; i += channels, j++)
pixelSetter(&img->pixels[j + frame*w*h], ptr + i * sizeof(unsigned char) + offset);
if (frames > 1) {
uint16_t delay = ptr[offset + (1 + i) * sizeof(unsigned char)] << 8;
delay += ptr[offset + i * sizeof(unsigned char)];
img->delays[frame] = delay;
}
}

stbi_image_free(ptr);
Expand Down Expand Up @@ -62,8 +204,11 @@ void img_copy(image_t* img, image_t *cpy)

void img_convert_colors(image_t *img)
{
for (uint32_t i = 0 ; i < img->width*img->height; i++)
convert_color(&(img->pixels[i]), &(img->pixels[i]));
for (uint32_t frame = 0; frame < img->frames; frame++) {
uint32_t offset = img->height*img->width * frame;
for (uint32_t i = 0 ; i < img->width*img->height; i++)
convert_color(&(img->pixels[i+offset]), &(img->pixels[i+offset]));
}
}

void img_free(image_t *img)
Expand All @@ -83,26 +228,30 @@ void img_resize(image_t *img, float wsc, float hsc)
hsc_i = hsc;
wh = hsc_i*wsc_i;

if (!(pix = malloc(sizeof(color_t)*w*h))) {
if (!(pix = malloc(sizeof(color_t)*w*h * img->frames))) {
perror("malloc error\n");
exit(1);
}
for (uint32_t y = 0; y < h; ++y) {
for (uint32_t x = 0; x < w; ++x) {
uint32_t r = 0, g = 0, b = 0, a = 0,
srcx = x*wsc,
srcy = y*hsc;
for (int32_t yi = 0; yi < hsc_i; ++yi)
for (int32_t xi = 0; xi < wsc_i; ++xi) {
r += img->pixels[srcx + xi +(srcy+yi)*img->width].r;
g += img->pixels[srcx + xi +(srcy+yi)*img->width].g;
b += img->pixels[srcx + xi +(srcy+yi)*img->width].b;
a += img->pixels[srcx + xi +(srcy+yi)*img->width].a;
}
pix[x+y*w].r = r/wh;
pix[x+y*w].g = g/wh;
pix[x+y*w].b = b/wh;
pix[x+y*w].a = a/wh;
for (uint32_t frame = 0; frame < img->frames; frame++) {
uint32_t src_offset = img->height*img->width * frame,
offset = w*h * frame;
for (uint32_t y = 0; y < h; ++y) {
for (uint32_t x = 0; x < w; ++x) {
uint32_t r = 0, g = 0, b = 0, a = 0,
srcx = x*wsc,
srcy = y*hsc;
for (int32_t yi = 0; yi < hsc_i; ++yi)
for (int32_t xi = 0; xi < wsc_i; ++xi) {
r += img->pixels[src_offset + srcx + xi +(srcy+yi)*img->width].r;
g += img->pixels[src_offset + srcx + xi +(srcy+yi)*img->width].g;
b += img->pixels[src_offset + srcx + xi +(srcy+yi)*img->width].b;
a += img->pixels[src_offset + srcx + xi +(srcy+yi)*img->width].a;
}
pix[offset + x+y*w].r = r/wh;
pix[offset + x+y*w].g = g/wh;
pix[offset + x+y*w].b = b/wh;
pix[offset + x+y*w].a = a/wh;
}
}
}
free(img->pixels);
Expand Down
5 changes: 3 additions & 2 deletions src/sh_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@
* @{
*/


/**
* @brief Structure to handle images
*/
typedef struct {
color_t *pixels; ///< 1 dim pixels pixels[x+y*w]
uint32_t width, ///< Width of the image
height; ///< Height of the image
height, ///< Height of the image
frames; ///< Number of frames
uint16_t *delays; ///< Array of delays. Length = frames - 1
} image_t;

/**
Expand Down

0 comments on commit 3ed2e75

Please sign in to comment.