Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
LO21_Pin_Noir_Boucher_Bouri_Detree
CellulutLO21
Commits
a1042598
Commit
a1042598
authored
Jun 12, 2021
by
Yann Boucher
Browse files
Support for saving images and animated GIF files
parent
41cb870f
Pipeline
#79726
passed with stages
in 18 seconds
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
forms/interface.ui
View file @
a1042598
...
...
@@ -138,6 +138,9 @@
<property
name=
"minimum"
>
<number>
1
</number>
</property>
<property
name=
"maximum"
>
<number>
100
</number>
</property>
<property
name=
"value"
>
<number>
20
</number>
</property>
...
...
@@ -621,7 +624,10 @@ pattern recorded :</string>
<property
name=
"title"
>
<string>
Edit
</string>
</property>
<addaction
name=
"separator"
/>
<addaction
name=
"actionCharger_depuis_une_image"
/>
<addaction
name=
"action_save_image"
/>
<addaction
name=
"action_save_gif"
/>
<addaction
name=
"action_save_struct"
/>
</widget>
<widget
class=
"QMenu"
name=
"menuA_propos"
>
...
...
@@ -682,6 +688,16 @@ pattern recorded :</string>
<string>
Play Snake
</string>
</property>
</action>
<action
name=
"action_save_image"
>
<property
name=
"text"
>
<string>
Save as an image...
</string>
</property>
</action>
<action
name=
"action_save_gif"
>
<property
name=
"text"
>
<string>
Save as an animated GIF...
</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
...
...
include/gridview.hpp
View file @
a1042598
...
...
@@ -139,6 +139,9 @@ public:
//! \brief Annule la dernière opération (effectue un Ctrl-Z).
void
undo
();
//! \brief Retourne une QImage représentant la grille actuelle.
const
QImage
&
grid_image
()
const
;
signals:
//! \brief Signal émis quand le zoom change.
//! \param cell_size la nouvelle taille à l'écran en pixels d'une cellule
...
...
@@ -159,8 +162,6 @@ private:
void
click_on
(
Coord
coord
);
void
update_current_mouse_pos
(
Coord
coord
);
const
QImage
&
grid_image
()
const
;
private
slots
:
void
handle_rubberband
(
QRect
,
QPointF
,
QPointF
);
...
...
include/history.h
View file @
a1042598
...
...
@@ -48,6 +48,18 @@ public:
//! \return Retourne le nombre de lignes de la Grille
unsigned
int
get_nbMax
()
const
{
return
nbMax
;}
//! \brief Retourne le nombre de grilles contenues dans l'historique.
unsigned
int
size
()
const
{
return
tab
.
size
();
}
//! \brief Accède à la grille d'indice i.
Grid
at
(
unsigned
int
i
)
const
{
if
(
i
>=
size
())
throw
HistoryException
(
"The stack is empty."
);
return
tab
[
i
];
}
//! \brief Ajoute une grille dans l'historique
void
pushGrid
(
const
Grid
&
g
);
...
...
include/interface.hpp
View file @
a1042598
...
...
@@ -54,6 +54,12 @@ private slots:
//! \returns Le nom du fichier choisi
QString
afficher_interface_sauvegarde_structure
();
//! \brief Sauvegarde la grille actuelle en tant qu'image.
void
save_as_image
();
//! \brief Sauvegarde l'historique d'exécution en tant que GIF animé.
void
save_as_gif
();
void
on_nbrStateComboBox_currentTextChanged
(
const
QString
&
arg1
);
//! \brief Créer une nouvelle grille ayant des états aléatoire
...
...
include/simulation.hpp
View file @
a1042598
...
...
@@ -61,7 +61,9 @@ public:
//! Vide également l'historique
void
setHistorySize
(
unsigned
int
size
)
{
hist
=
History
(
size
);}
//! \brief Retourne une référence constante vers l'History actuel.
const
History
&
getHistory
()
const
{
return
hist
;
}
//! \brief Définit la grille
//!
...
...
src/gif/README
0 → 100644
View file @
a1042598
Source: https://github.com/lecram/gifenc
GIF encoder
===========
This is a small C library that can be used to create GIF animations.
Features
--------
* user-defined palette of any depth from 1 up to 8
* each frame has its own (user-specified) delay time
* flexible looping options: no loop, N repetitions, infinite loop
* GIF size optimization: only stores frame differences
* memory efficient: saves frames to file as soon as possible
* small and portable: less than 300 lines of C99
* public domain
Limitations
-----------
* no frame-local palettes (incompatible with size optimization)
* no interlacing (bad for compression, useless for animations)
Documentation
-------------
There are only three functions declared in "gifenc.h": ge_new_gif(),
ge_add_frame() and ge_close_gif().
The ge_new_gif() function receives GIF global options and returns a ge_GIF
handler:
ge_GIF *ge_new_gif(
const char *fname, /* GIF file name */
uint16_t width, uint16_t height, /* frame size */
uint8_t *palette, int depth, /* color table */
int loop /* looping information */
);
The `palette` parameter must point to an array of color data. Each entry is a
24-bits RGB color, stored as three contiguous bytes: the first is the red value
(0-255), then green, then blue. Entries are stored in a contiguous byte array.
The `depth` parameter specifies how many colors are present in the given
palette. The number of color entries must be 2 ^ depth, where 1 <= depth <= 8.
Example `palette` and `depth` values:
uint8_t palette[] = {
0x00, 0x00, 0x00, /* entry 0: black */
0xFF, 0xFF, 0xFF, /* entry 1: white */
0xFF, 0x00, 0x00, /* entry 2: red */
0x00, 0x00, 0xFF, /* entry 3: blue */
};
int depth = 2; /* palette has 1 << 2 (i.e. 4) entries */
If `palette` is NULL, entries are taken from a default table of 256 colors. If
`depth` < 8, the default table will be truncated to the appropriate size. The
default table is composed of the 16 standard VGA colors, plus the 216 web-safe
colors (all combinations of RGB with only 6 valid values per channel), plus 24
grey colors equally spaced between black and white, excluding both.
If `depth` < 0 and `palette` is not NULL, then the default table with 2 ^ -depth
colors is used and it is stored in the array at the `palette` address.
If the `loop` parameter is zero, the resulting GIF will loop forever. If it is a
positive number, the animation will be played that number of times. If `loop`
is negative, no looping information is stored in the GIF file (for most GIF
viewers, this is equivalent to `loop` == 1, i.e., "play once").
The ge_add_frame() function reads pixel data from a buffer and saves the
resulting frame to the file associated with the given ge_GIF handler:
void ge_add_frame(ge_GIF *gif, uint16_t delay);
The `delay` parameter specifies how long the frame will be shown, in hundreths
of a second. For example, `delay` == 100 means "show this frame for one second"
and `delay` == 25 means "show this frame for a quarter of a second". Note that
short delays may not be supported by some GIF viewers: it's recommended to keep
a minimum of `delay` == 6. If `delay` == 0, no delay information will be stored
for the frame. This can be used when creating still (single-frame) GIF images.
Pixel data is read from `gif->frame`, which points to a memory block like this:
uint8_t _frame_[gif->width * gif->height];
Note that the address of `gif->frame` changes between calls to ge_add_frame()
(*). For this reason, each frame must be written in its entirety to the current
address, even if one only wants to change a few pixels from the last frame. The
encoder will automatically detect the difference between two consecutive frames
in order to minimize the size of the output.
Each byte in the frame buffer represents a pixel. The value of each pixel is an
index to a palette entry. For instance, given the example palette above, we can
create a frame displaying a red-on-black "F" letter like this:
uint8_t pixels[] = {
2, 2, 2, 2,
2, 0, 0, 0,
2, 0, 0, 0,
2, 2, 2, 0,
2, 0, 0, 0,
2, 0, 0, 0,
2, 0, 0, 0
};
ge_GIF *gif = ge_new_gif("F.gif", 4, 7, palette, depth, -1);
memcpy(gif->frame, pixels, sizeof(pixels));
ge_add_frame(gif, 0);
ge_close_gif(gif);
The function ge_close_gif() finishes writting GIF data to the file associated
with the given ge_GIF handler and does memory clean-up. This function must be
called once after all desired frames have been added, in order to correctly save
the GIF file. After calling this function, the ge_GIF handler cannot be used
anymore.
void ge_close_gif(ge_GIF* gif);
(*) The encoder keeps two frame buffers internally, in order to implement the
size optimization. The address of `gif->frame` alternates between those two
buffers after each call to ge_add_frame().
Example
-------
See the file "example.c". It can be tested like this:
$ cc -o example gifenc.c example.c
$ ./example
That should create an animated GIF named "example.gif".
Copying
-------
All of the source code and documentation for gifenc is released into the
public domain and provided without warranty of any kind.
src/gif/gifenc.c
0 → 100644
View file @
a1042598
#include
"gifenc.h"
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<fcntl.h>
#ifdef _WIN32
#include
<io.h>
#else
#include
<unistd.h>
#endif
/* helper to write a little-endian 16-bit number portably */
#define write_num(fd, n) write((fd), (uint8_t []) {(n) & 0xFF, (n) >> 8}, 2)
static
uint8_t
vga
[
0x30
]
=
{
0x00
,
0x00
,
0x00
,
0xAA
,
0x00
,
0x00
,
0x00
,
0xAA
,
0x00
,
0xAA
,
0x55
,
0x00
,
0x00
,
0x00
,
0xAA
,
0xAA
,
0x00
,
0xAA
,
0x00
,
0xAA
,
0xAA
,
0xAA
,
0xAA
,
0xAA
,
0x55
,
0x55
,
0x55
,
0xFF
,
0x55
,
0x55
,
0x55
,
0xFF
,
0x55
,
0xFF
,
0xFF
,
0x55
,
0x55
,
0x55
,
0xFF
,
0xFF
,
0x55
,
0xFF
,
0x55
,
0xFF
,
0xFF
,
0xFF
,
0xFF
,
0xFF
,
};
struct
Node
{
uint16_t
key
;
struct
Node
*
children
[];
};
typedef
struct
Node
Node
;
static
Node
*
new_node
(
uint16_t
key
,
int
degree
)
{
Node
*
node
=
calloc
(
1
,
sizeof
(
*
node
)
+
degree
*
sizeof
(
Node
*
));
if
(
node
)
node
->
key
=
key
;
return
node
;
}
static
Node
*
new_trie
(
int
degree
,
int
*
nkeys
)
{
Node
*
root
=
new_node
(
0
,
degree
);
/* Create nodes for single pixels. */
for
(
*
nkeys
=
0
;
*
nkeys
<
degree
;
(
*
nkeys
)
++
)
root
->
children
[
*
nkeys
]
=
new_node
(
*
nkeys
,
degree
);
*
nkeys
+=
2
;
/* skip clear code and stop code */
return
root
;
}
static
void
del_trie
(
Node
*
root
,
int
degree
)
{
if
(
!
root
)
return
;
for
(
int
i
=
0
;
i
<
degree
;
i
++
)
del_trie
(
root
->
children
[
i
],
degree
);
free
(
root
);
}
#define write_and_store(s, dst, fd, src, n) \
do { \
write(fd, src, n); \
if (s) { \
memcpy(dst, src, n); \
dst += n; \
} \
} while (0);
static
void
put_loop
(
ge_GIF
*
gif
,
uint16_t
loop
);
ge_GIF
*
ge_new_gif
(
const
char
*
fname
,
uint16_t
width
,
uint16_t
height
,
uint8_t
*
palette
,
int
depth
,
int
loop
)
{
int
i
,
r
,
g
,
b
,
v
;
int
store_gct
,
custom_gct
;
ge_GIF
*
gif
=
calloc
(
1
,
sizeof
(
*
gif
)
+
2
*
width
*
height
);
if
(
!
gif
)
goto
no_gif
;
gif
->
w
=
width
;
gif
->
h
=
height
;
gif
->
frame
=
(
uint8_t
*
)
&
gif
[
1
];
gif
->
back
=
&
gif
->
frame
[
width
*
height
];
#ifdef _WIN32
gif
->
fd
=
creat
(
fname
,
S_IWRITE
);
#else
gif
->
fd
=
creat
(
fname
,
0666
);
#endif
if
(
gif
->
fd
==
-
1
)
goto
no_fd
;
#ifdef _WIN32
setmode
(
gif
->
fd
,
O_BINARY
);
#endif
write
(
gif
->
fd
,
"GIF89a"
,
6
);
write_num
(
gif
->
fd
,
width
);
write_num
(
gif
->
fd
,
height
);
store_gct
=
custom_gct
=
0
;
if
(
palette
)
{
if
(
depth
<
0
)
store_gct
=
1
;
else
custom_gct
=
1
;
}
if
(
depth
<
0
)
depth
=
-
depth
;
gif
->
depth
=
depth
>
1
?
depth
:
2
;
write
(
gif
->
fd
,
(
uint8_t
[])
{
0xF0
|
(
depth
-
1
),
0x00
,
0x00
},
3
);
if
(
custom_gct
)
{
write
(
gif
->
fd
,
palette
,
3
<<
depth
);
}
else
if
(
depth
<=
4
)
{
write_and_store
(
store_gct
,
palette
,
gif
->
fd
,
vga
,
3
<<
depth
);
}
else
{
write_and_store
(
store_gct
,
palette
,
gif
->
fd
,
vga
,
sizeof
(
vga
));
i
=
0x10
;
for
(
r
=
0
;
r
<
6
;
r
++
)
{
for
(
g
=
0
;
g
<
6
;
g
++
)
{
for
(
b
=
0
;
b
<
6
;
b
++
)
{
write_and_store
(
store_gct
,
palette
,
gif
->
fd
,
((
uint8_t
[])
{
r
*
51
,
g
*
51
,
b
*
51
}),
3
);
if
(
++
i
==
1
<<
depth
)
goto
done_gct
;
}
}
}
for
(
i
=
1
;
i
<=
24
;
i
++
)
{
v
=
i
*
0xFF
/
25
;
write_and_store
(
store_gct
,
palette
,
gif
->
fd
,
((
uint8_t
[])
{
v
,
v
,
v
}),
3
);
}
}
done_gct:
if
(
loop
>=
0
&&
loop
<=
0xFFFF
)
put_loop
(
gif
,
(
uint16_t
)
loop
);
return
gif
;
no_fd:
free
(
gif
);
no_gif:
return
NULL
;
}
static
void
put_loop
(
ge_GIF
*
gif
,
uint16_t
loop
)
{
write
(
gif
->
fd
,
(
uint8_t
[])
{
'!'
,
0xFF
,
0x0B
},
3
);
write
(
gif
->
fd
,
"NETSCAPE2.0"
,
11
);
write
(
gif
->
fd
,
(
uint8_t
[])
{
0x03
,
0x01
},
2
);
write_num
(
gif
->
fd
,
loop
);
write
(
gif
->
fd
,
"
\0
"
,
1
);
}
/* Add packed key to buffer, updating offset and partial.
* gif->offset holds position to put next *bit*
* gif->partial holds bits to include in next byte */
static
void
put_key
(
ge_GIF
*
gif
,
uint16_t
key
,
int
key_size
)
{
int
byte_offset
,
bit_offset
,
bits_to_write
;
byte_offset
=
gif
->
offset
/
8
;
bit_offset
=
gif
->
offset
%
8
;
gif
->
partial
|=
((
uint32_t
)
key
)
<<
bit_offset
;
bits_to_write
=
bit_offset
+
key_size
;
while
(
bits_to_write
>=
8
)
{
gif
->
buffer
[
byte_offset
++
]
=
gif
->
partial
&
0xFF
;
if
(
byte_offset
==
0xFF
)
{
write
(
gif
->
fd
,
"
\xFF
"
,
1
);
write
(
gif
->
fd
,
gif
->
buffer
,
0xFF
);
byte_offset
=
0
;
}
gif
->
partial
>>=
8
;
bits_to_write
-=
8
;
}
gif
->
offset
=
(
gif
->
offset
+
key_size
)
%
(
0xFF
*
8
);
}
static
void
end_key
(
ge_GIF
*
gif
)
{
int
byte_offset
;
byte_offset
=
gif
->
offset
/
8
;
if
(
gif
->
offset
%
8
)
gif
->
buffer
[
byte_offset
++
]
=
gif
->
partial
&
0xFF
;
if
(
byte_offset
)
{
write
(
gif
->
fd
,
(
uint8_t
[])
{
byte_offset
},
1
);
write
(
gif
->
fd
,
gif
->
buffer
,
byte_offset
);
}
write
(
gif
->
fd
,
"
\0
"
,
1
);
gif
->
offset
=
gif
->
partial
=
0
;
}
static
void
put_image
(
ge_GIF
*
gif
,
uint16_t
w
,
uint16_t
h
,
uint16_t
x
,
uint16_t
y
)
{
int
nkeys
,
key_size
,
i
,
j
;
Node
*
node
,
*
child
,
*
root
;
int
degree
=
1
<<
gif
->
depth
;
write
(
gif
->
fd
,
","
,
1
);
write_num
(
gif
->
fd
,
x
);
write_num
(
gif
->
fd
,
y
);
write_num
(
gif
->
fd
,
w
);
write_num
(
gif
->
fd
,
h
);
write
(
gif
->
fd
,
(
uint8_t
[])
{
0x00
,
gif
->
depth
},
2
);
root
=
node
=
new_trie
(
degree
,
&
nkeys
);
key_size
=
gif
->
depth
+
1
;
put_key
(
gif
,
degree
,
key_size
);
/* clear code */
for
(
i
=
y
;
i
<
y
+
h
;
i
++
)
{
for
(
j
=
x
;
j
<
x
+
w
;
j
++
)
{
uint8_t
pixel
=
gif
->
frame
[
i
*
gif
->
w
+
j
]
&
(
degree
-
1
);
child
=
node
->
children
[
pixel
];
if
(
child
)
{
node
=
child
;
}
else
{
put_key
(
gif
,
node
->
key
,
key_size
);
if
(
nkeys
<
0x1000
)
{
if
(
nkeys
==
(
1
<<
key_size
))
key_size
++
;
node
->
children
[
pixel
]
=
new_node
(
nkeys
++
,
degree
);
}
else
{
put_key
(
gif
,
degree
,
key_size
);
/* clear code */
del_trie
(
root
,
degree
);
root
=
node
=
new_trie
(
degree
,
&
nkeys
);
key_size
=
gif
->
depth
+
1
;
}
node
=
root
->
children
[
pixel
];
}
}
}
put_key
(
gif
,
node
->
key
,
key_size
);
put_key
(
gif
,
degree
+
1
,
key_size
);
/* stop code */
end_key
(
gif
);
del_trie
(
root
,
degree
);
}
static
int
get_bbox
(
ge_GIF
*
gif
,
uint16_t
*
w
,
uint16_t
*
h
,
uint16_t
*
x
,
uint16_t
*
y
)
{
int
i
,
j
,
k
;
int
left
,
right
,
top
,
bottom
;
left
=
gif
->
w
;
right
=
0
;
top
=
gif
->
h
;
bottom
=
0
;
k
=
0
;
for
(
i
=
0
;
i
<
gif
->
h
;
i
++
)
{
for
(
j
=
0
;
j
<
gif
->
w
;
j
++
,
k
++
)
{
if
(
gif
->
frame
[
k
]
!=
gif
->
back
[
k
])
{
if
(
j
<
left
)
left
=
j
;
if
(
j
>
right
)
right
=
j
;
if
(
i
<
top
)
top
=
i
;
if
(
i
>
bottom
)
bottom
=
i
;
}
}
}
if
(
left
!=
gif
->
w
&&
top
!=
gif
->
h
)
{
*
x
=
left
;
*
y
=
top
;
*
w
=
right
-
left
+
1
;
*
h
=
bottom
-
top
+
1
;
return
1
;
}
else
{
return
0
;
}
}
static
void
set_delay
(
ge_GIF
*
gif
,
uint16_t
d
)
{
write
(
gif
->
fd
,
(
uint8_t
[])
{
'!'
,
0xF9
,
0x04
,
0x04
},
4
);
write_num
(
gif
->
fd
,
d
);
write
(
gif
->
fd
,
"
\0\0
"
,
2
);
}
void
ge_add_frame
(
ge_GIF
*
gif
,
uint16_t
delay
)
{
uint16_t
w
,
h
,
x
,
y
;
uint8_t
*
tmp
;
if
(
delay
)
set_delay
(
gif
,
delay
);
if
(
gif
->
nframes
==
0
)
{
w
=
gif
->
w
;
h
=
gif
->
h
;
x
=
y
=
0
;
}
else
if
(
!
get_bbox
(
gif
,
&
w
,
&
h
,
&
x
,
&
y
))
{
/* image's not changed; save one pixel just to add delay */
w
=
h
=
1
;
x
=
y
=
0
;
}
put_image
(
gif
,
w
,
h
,
x
,
y
);
gif
->
nframes
++
;
tmp
=
gif
->
back
;
gif
->
back
=
gif
->
frame
;
gif
->
frame
=
tmp
;
}
void
ge_close_gif
(
ge_GIF
*
gif
)
{
write
(
gif
->
fd
,
";"
,
1
);
close
(
gif
->
fd
);
free
(
gif
);
}
src/gif/gifenc.h
0 → 100644
View file @
a1042598
#ifndef GIFENC_H
#define GIFENC_H
#include
<stdint.h>
#ifdef __cplusplus
extern
"C"
{
#endif
typedef
struct
ge_GIF
{
uint16_t
w
,
h
;
int
depth
;
int
fd
;
int
offset
;
int
nframes
;
uint8_t
*
frame
,
*
back
;
uint32_t
partial
;
uint8_t
buffer
[
0xFF
];
}
ge_GIF
;
ge_GIF
*
ge_new_gif
(
const
char
*
fname
,
uint16_t
width
,
uint16_t
height
,
uint8_t
*
palette
,
int
depth
,
int
loop
);
void
ge_add_frame
(
ge_GIF
*
gif
,
uint16_t
delay
);
void
ge_close_gif
(
ge_GIF
*
gif
);
#ifdef __cplusplus
}
#endif
#endif
/* GIFENC_H */
src/interface.cpp
View file @
a1042598
#include
"interface.hpp"
#
include
"interface.hpp"
#include
"ui_interface.h"
#include
"savingdialog.hpp"
...
...
@@ -13,6 +13,8 @@
#include
"neighborhoodDialog.hpp"
#include
"colorlabel.h"
#include
"gif/gifenc.h"
#include
<QtGlobal>
#include
<QJsonArray>
#include
<QDate>
...
...
@@ -78,6 +80,8 @@ MainWindow::MainWindow(QWidget *parent)
{
load_from_image
();
});
connect
(
ui
->
action_save_image
,
&
QAction
::
triggered
,
this
,
&
MainWindow
::
save_as_image
);
connect
(
ui
->
action_save_gif
,
&
QAction
::
triggered
,
this
,
&
MainWindow
::
save_as_gif
);
connect
(
ui
->
border_combo
,
QOverload
<
int
>::
of
(
&
QComboBox
::
activated
),
this
,
&
MainWindow
::
load_boundary_policy
);
ui
->
struct_library
->
update_cell_pixel_size
(
ui
->
grid_view
->
cell_screen_size
());
...
...
@@ -192,6 +196,62 @@ QString MainWindow::afficher_interface_sauvegarde_structure()
return
filename
;
}
void
MainWindow
::
save_as_image
()
{