Compare commits
25 Commits
edb1a007c0
...
84902bd0dc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84902bd0dc | ||
|
|
495f3c5e9d | ||
|
|
15cbe1d4da | ||
|
|
9be18e796d | ||
|
|
137bcf1417 | ||
|
|
c22eabb1aa | ||
|
|
c0bf9b0d2e | ||
|
|
916740069a | ||
|
|
70ec238654 | ||
|
|
6e35f31f34 | ||
|
|
62563c0ba6 | ||
|
|
b83b5a60c5 | ||
|
|
3d0d8a8362 | ||
|
|
77c2789ce3 | ||
|
|
e4df136bb0 | ||
|
|
a76986e0c0 | ||
|
|
a84b4a79b2 | ||
|
|
336d266c5f | ||
|
|
6b53a0cd78 | ||
|
|
53910acd6a | ||
|
|
962c461e5c | ||
|
|
875169d2c8 | ||
|
|
eb9a9e796b | ||
|
|
81d7a4ca6c | ||
|
|
75c10c0311 |
181
labs/list_iterators_new/README.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Lab 4 — Reversing Data in Different Ways: STL Vectors vs. STL Lists
|
||||
|
||||
## Background - Introduction to GIF Formats and the Concept of Frames
|
||||
|
||||
## What is a GIF?
|
||||
|
||||
A **GIF** (Graphics Interchange Format) is a popular bitmap image format commonly used for its ability to support animations. It was introduced in 1987 by CompuServe and has since become widely used on the internet. GIFs support **256 colors** and are often used for simple graphics, icons, or short animations that loop seamlessly. The GIF format is best known for its lightweight nature and ability to be easily shared on social media platforms and websites.
|
||||
|
||||
## GIF Frames and Animation
|
||||
|
||||
A GIF image file is composed of a sequence of **frames**. Each frame is a static image that, when displayed in sequence, creates the illusion of movement. The concept of frames in a GIF is similar to how traditional film or digital video works, where individual frames are shown at a rapid pace to simulate continuous motion.
|
||||
|
||||
### Key Concepts:
|
||||
|
||||
- **Frames**: Each individual image in a GIF sequence. Each frame may display a single static image or a portion of the animation.
|
||||
- **Frame Duration**: Each frame in a GIF has a duration (in milliseconds), determining how long it is displayed before transitioning to the next frame. The timing of each frame is crucial in defining the speed of the animation.
|
||||
- **Looping**: GIFs can be set to loop indefinitely or stop after a certain number of loops. This makes GIFs ideal for short animations that need to play continuously.
|
||||
|
||||
## Lab Tasks
|
||||
|
||||
In this lab, you will modify an existing C++ program. This program reverses a GIF image file.
|
||||
|
||||
Here are examples of reversing GIF.
|
||||
|
||||
Example 1:
|
||||
|
||||
This is the [original GIF](dog_good.gif). And this is the [reversed GIF](dog_bad.gif).
|
||||
|
||||
Example 2:
|
||||
|
||||
This is the [original GIF](door_break.gif). And this is the [reversed GIF](door_restore.gif).
|
||||
|
||||
Example 3:
|
||||
|
||||
This is the [original GIF](jump_real.gif). And this is the [reversed GIF](jump_fake.gif).
|
||||
|
||||
Example 4:
|
||||
|
||||
This is the [original GIF](brick_real.gif). And this is the [reversed GIF](brick_fake.gif).
|
||||
|
||||
Example 5:
|
||||
|
||||
This is the [original GIF](seat_real.gif). And this is the [reversed GIF](seat_fake.gif) - "I am sorry grandma, I want to take my seat back."
|
||||
|
||||
Example 6:
|
||||
|
||||
This is the [original GIF](pool_real.gif). And this is the [reversed GIF](pool_fake.gif).
|
||||
|
||||
Example 7:
|
||||
|
||||
This is the [original GIF](girl_real.gif). And this is the [reversed GIF](girl_fake.gif).
|
||||
|
||||
## Starter Code
|
||||
|
||||
The starter code [main.cpp](main.cpp) defines a class named GifFrame to represent GIF frames. Each object of this class represents one frame.
|
||||
|
||||
```cpp
|
||||
class GifFrame {
|
||||
public:
|
||||
std::vector<uint8_t> data; // raw image data
|
||||
int width, height; // frame dimensions
|
||||
int left, top; // position within the GIF
|
||||
uint8_t disposalMethod = 0; // add this field (0 = undefined, 1 = keep, etc.)
|
||||
bool hasTransparency = false; // flag for transparency
|
||||
uint8_t transparentColorIndex = 0; // index of the transparent color in the global color table
|
||||
uint16_t delayTime = 0; // delay time before the next frame in hundredths of a second
|
||||
// default constructor
|
||||
GifFrame() : width(0), height(0), left(0), top(0), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
// constructor to initialize the frame data
|
||||
GifFrame(const std::vector<uint8_t>& frameData) : data(frameData), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
};
|
||||
```
|
||||
|
||||
When given an input GIF image file, the starter code extracts all frames from this image file, and store these frames in an std::vector<GifFrame> named frames. This frames vector will then be passed to a function *reverseFrames* which reverses the vector. The *reverseFrames* function will return an std::vector<GifFrame> which contains the reversed frames. This reversed frames vector is then passed to a function writeGif(), which just writes all frames in a new GIF image file.
|
||||
|
||||
In this lab, multiple copies of the starter code will be provided, but you will only be working on the *reverseFrames* function - you will be required to re-write this function in different ways.
|
||||
|
||||
**Note**, the given program expects the name of the input file to be *input.gif*, and it will produce an output file named *reversed.gif*. You can compile and run the program like this:
|
||||
|
||||
```console
|
||||
$ g++ main.cpp
|
||||
$ ./a.out
|
||||
GIF successfully written to reversed.gif
|
||||
```
|
||||
|
||||
## Checkpoint 1: Reverse with STL Vector Swaps
|
||||
*estimate: TBD*
|
||||
|
||||
Complete the *reverseFrames* function in [main_cp1.cpp](main_cp1.cpp). For this checkpoint, use indexing/subscripting/[] on the vector, not iterators (or pointers). You may not use a second vector or array or list. The trick is to step through the vector one location at a time, swapping values between the first half of the
|
||||
vector and the second half. As examples, the value at location 0 and the value at location size()-1 must
|
||||
be swapped, and the value at location 1 and the value at location size()-2 must be swapped.
|
||||
Make sure your code works with even and odd length vectors.
|
||||
|
||||
**To complete this checkpoint**, show a TA/mentor your debugged functions to reverse STL vectors by element swapping. TA/mentor will specify one original GIF image file, and you rename that file to *input.gif*. After that you run your program, and it should produce the reversed GIF, show the reversed GIF to the TA/mentor.
|
||||
|
||||
## Checkpoint 2: Reverse with STL List Swaps
|
||||
*estimate: TBD*
|
||||
|
||||
Complete the *reverseFrames* function in [main_cp2.cpp](main_cp2.cpp). For this checkpoint, now the *reverseFrames* function takes an std::list as its parameter, and your task is to reverse this list.
|
||||
|
||||
You may want to use a straightforward concept we did not discuss in lecture: a reverse iterator. A reverse
|
||||
iterator is designed to step through a list from the back to the front. An example will make the main
|
||||
properties clear:
|
||||
|
||||
```cpp
|
||||
std::list<int> a;
|
||||
unsigned int i;
|
||||
for ( i=1; i<10; ++i ){
|
||||
a.push_back( i*i );
|
||||
}
|
||||
std::list<int>::reverse_iterator ri;
|
||||
for( ri = a.rbegin(); ri != a.rend(); ++ri ){
|
||||
std::cout << *ri << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
This code will print out the values 81, 64, 49, . . . , 1, in order, on separate lines. You can also compile and run this [example program](reverse_iterator.cpp).
|
||||
|
||||
Observe the type for the reverse iterator, the use of the functions rbegin and rend to provide iterators that delimit the bounds on
|
||||
the reverse iterator, and the use of the ++ operator to take one step backwards through the list. It is very
|
||||
important to realize that rbegin and end are NOT the same thing! One of the challenges here will be
|
||||
determining when to stop (when you’ve reached the halfway point in the list). You may use an integer
|
||||
counter variable to help you do this.
|
||||
|
||||
For this checkpoint you should not use *erase*, or *insert*, or the *push* or *pop* functions.
|
||||
<!-- Note, you’ll probably need to add the keyword typename in front of your templated iterator types to unconfuse the compiler.
|
||||
|
||||
```cpp
|
||||
typename std::list<T>::iterator itr = data.begin();
|
||||
```
|
||||
-->
|
||||
|
||||
**To complete this checkpoint**, show a TA/mentor your debugged functions to reverse STL lists by element swapping. TA/mentor will specify one original GIF image file, and you rename that file to *input.gif*. After that you run your program, and it should produce the reversed GIF, show the reversed GIF to the TA/mentor.
|
||||
|
||||
## Checkpoint 3: Reverse with STL List Using Insert/Erase/Push/Pop
|
||||
*estimate: TBD*
|
||||
|
||||
Form a team of 4. You may form a team of 2 or 3 only with approval from your graduate lab TA.
|
||||
|
||||
Each student should make a copy of their solution file for Checkpoint 2. And then, each student should
|
||||
rewrite their STL list reverse function:
|
||||
- **STUDENT 1** Using only front(), pop_front(), and insert().
|
||||
- **STUDENT 2** Using only back(), pop_back(), insert(), and iterator increment.
|
||||
- **STUDENT 3** Using only erase(), push_front(), and iterator dereference.
|
||||
- **STUDENT 4** Using only erase(), push_back(), iterator dereference, and iterator decrement.
|
||||
Each of these solutions is allowed to use a for or while loop and a temporary variable to store a single
|
||||
element, but should not use any auxiliary data structures (no array, no vector, no additional list, etc.)
|
||||
|
||||
Note that these solutions are quite different than the algorithms that reverse a vector or list by swapping
|
||||
values. Test and debug your own code before helping your teammates. Discuss the similarities and differences between
|
||||
the solutions to each version of the reverse function.
|
||||
|
||||
**To complete this checkpoint**, as a team, present your debugged solutions to a TA or mentor. TA/mentor will specify one original GIF image file, and you rename that file to *input.gif*. After that you run your program, and it should produce the reversed GIF, show the reversed GIF to the TA/mentor.
|
||||
|
||||
<!-- ## Checkpoint 3: Reversing a Homemade Linked List
|
||||
*estimate: TBD*
|
||||
|
||||
This checkpoint is an individual checkpoint. (But you can ask your teammates questions if you get stuck.)
|
||||
Pull out some paper. Following the conventions from lecture, draw a picture of a “homemade” singly-linked
|
||||
list that stores the values 1, 2, 3, and 4. Make a variable on the stack named my_list of type Node* that
|
||||
points to the first node in the chain (the node storing the value 1). The 4 node objects should be separate
|
||||
blobs of memory dynamically-allocated on the heap.
|
||||
|
||||
Now, modify this diagram to reverse the list – you can do this by only changing pointers! You should use the
|
||||
existing node objects. Don’t copy the entire diagram or make any new nodes. You should not change the
|
||||
values inside of any node – don’t swap values like we did for Checkpoint 1.
|
||||
Then, write pseudo-code to reverse this list, just changing the pointers as you diagrammed above. You may
|
||||
use helper variables (of type Node*) but no other data structures or variables. Remember that when we
|
||||
directly manipulate homemade linked lists we don’t use iterators.
|
||||
|
||||
Finally, read the starter code of checkpoint 3: [checkpoint3.cpp](checkpoint3.cpp). Complete the reverse function using your diagram and pseudocode as a guide. Test and debug the code. Add a few additional test cases to the main function to ensure your code works with an empty list,
|
||||
and lists with one or two values. Also add a test or two of a node chain with something other than ints.
|
||||
If you have time, write 2 versions of this function, one version should be iterative (using a for or while loop)
|
||||
and one version should be recursive.
|
||||
|
||||
**Note**: this reverse function takes a pointer as its argument, but we are passing this pointer by reference, because we want to modify this pointer. To understand the concept of passing a pointer by reference, you are recommended to read and run this [example program](reference_to_a_pointer.cpp).
|
||||
|
||||
**To complete this checkpoint**, show a TA or mentor your diagram and your debugged function(s) to
|
||||
reverse a homemade singly-linked list.-->
|
||||
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 6.0 MiB |
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 6.0 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
BIN
labs/list_iterators_new/girl_fake.gif
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
labs/list_iterators_new/girl_real.gif
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 13 MiB After Width: | Height: | Size: 13 MiB |
|
Before Width: | Height: | Size: 13 MiB After Width: | Height: | Size: 13 MiB |
@@ -2,7 +2,8 @@
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
struct GifFrame {
|
||||
class GifFrame {
|
||||
public:
|
||||
std::vector<uint8_t> data; // raw image data
|
||||
int width, height; // frame dimensions
|
||||
int left, top; // position within the GIF
|
||||
@@ -63,11 +64,12 @@ std::vector<uint8_t> extractGifHeader(const std::vector<uint8_t>& fileData) {
|
||||
return header;
|
||||
}
|
||||
|
||||
// this function reverses a vector using a reverse iterator.
|
||||
std::vector<GifFrame> reverseFrames(std::vector<GifFrame>& originalFrames) {
|
||||
std::vector<GifFrame> reversedFrames;
|
||||
|
||||
// Simply reverse the order of the frames
|
||||
for (auto it = originalFrames.rbegin(); it != originalFrames.rend(); ++it) {
|
||||
for (std::vector<GifFrame>::reverse_iterator it = originalFrames.rbegin(); it != originalFrames.rend(); ++it) {
|
||||
reversedFrames.push_back(*it);
|
||||
}
|
||||
|
||||
@@ -98,19 +100,10 @@ void writeGif(const std::string& outputFilename, const std::vector<uint8_t>& ori
|
||||
// extracts the size of the global color table:
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
outFile.write(reinterpret_cast<const char*>(&originalFileData[pos]), gctSize);
|
||||
std::cout << "Found a global color table\n";
|
||||
std::cout << "Global Color Table Size: " << gctSize << " bytes\n";
|
||||
pos += gctSize;
|
||||
}
|
||||
|
||||
if (!loopExtension.empty()) {
|
||||
std::cout << "Writing Netscape Loop Extension...\n";
|
||||
std::cout << "Loop Extension Bytes: ";
|
||||
for (uint8_t byte : loopExtension) {
|
||||
printf("%02X ", byte);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
outFile.write(reinterpret_cast<const char*>(loopExtension.data()), loopExtension.size());
|
||||
}
|
||||
|
||||
@@ -166,7 +159,6 @@ std::vector<GifFrame> extractFrames(const std::string& filename, std::vector<uin
|
||||
// if GCT Flag is set (GCT is present).
|
||||
if (packedFields & 0x80) {
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
std::cout << "Global Color Table Size: " << gctSize << " bytes\n";
|
||||
printPalette(fileData, 13, gctSize); // Starting at position 13 (after the logical screen descriptor)
|
||||
pos += gctSize;
|
||||
}
|
||||
@@ -244,13 +236,6 @@ std::vector<GifFrame> extractFrames(const std::string& filename, std::vector<uin
|
||||
|
||||
// Store the full extension data
|
||||
loopExtension.assign(extensionData.begin(), extensionData.end());
|
||||
// Debugging output
|
||||
std::cout << "Loop Extension Size: " << loopExtension.size() << "\n";
|
||||
std::cout << "Loop Extension Bytes: ";
|
||||
for (uint8_t byte : loopExtension) {
|
||||
printf("%02X ", byte);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} else { // Other extension blocks
|
||||
pos += 2;
|
||||
@@ -301,89 +286,9 @@ int main() {
|
||||
// extract the complete header from fileData
|
||||
std::vector<uint8_t> completeHeader = extractGifHeader(fileData);
|
||||
|
||||
// here header size should be 13 bytes.
|
||||
// GIF Header Structure: all GIF files must start with a header block. The header takes up the first six bytes of the file.
|
||||
// Byte 0-3: The first three bytes are called the signature. These should always be "GIF" (ie 47="G", 49="I", 46="F").
|
||||
// Byte 4-6: The next three specify the version of the specification that was used to encode the image.
|
||||
// Normally the version string will be either "89a" (ie 38="8", 39="9",61="a") or "87a" (ie 38="8", 37="7",61="a").
|
||||
// Byte: 7-12 The logical screen descriptor always immediately follows the header.
|
||||
// This block tells the decoder how much room this image will take up. It is exactly seven bytes long. It starts with the canvas width and canvas height. These value can be found in the first two pairs of two bytes each. Both are 16-bit, nonnegative integers (0-65,535).
|
||||
// The next byte contains four fields of packed data, the "logical screen descriptor".
|
||||
// The next byte gives us the background color index. This byte is only meaningful if the global color table flag is 1, and if there is no global color table, this byte should be 0.
|
||||
// The first 13 bytes of a standard GIF file are fixed and contain the following data:
|
||||
// Bytes 0-2: The signature "GIF" (3 bytes)
|
||||
// Bytes 3-5: The version, typically "87a" or "89a" (3 bytes)
|
||||
// Bytes 6-7: Logical screen width (2 bytes)
|
||||
// Bytes 8-9: Logical screen height (2 bytes)
|
||||
// Byte 10: Packed fields of the logical screen descriptor (1 byte)
|
||||
// Byte 11: Background color index (1 byte)
|
||||
// Byte 12: Pixel aspect ratio (1 byte)
|
||||
// After the 13 bytes, we have Graphic control extension blocks, which are used to specify transparency settings and control animations.
|
||||
// After the graphic control extension blocks, we have images. A single GIF file may contain multiple images.
|
||||
// Each image begins with an image descriptor block. This block is exactly 10 bytes long.
|
||||
// The first byte is the image separator. Every image descriptor begins with the value 2C. The next 8 bytes represent the location and size of the following image.
|
||||
// The last byte of the image descriptor is another packed field.
|
||||
// The first (most significant) bit in the byte is the local color table flag. Setting this flag to 1 allows you to specify that the image data that follows uses a different color table than the global color table.
|
||||
// A local color table is organized the same as a global color table. The local color table would always immediately follow an image descriptor but will only be there if the local color table flag is set to 1. It is effective only for the block of image data that immediately follows it. If no local color table is specified, the global color table is used for the following image data.
|
||||
// The size of the local color table can be calculated by the value given in the image descriptor.
|
||||
// Finally we get to the actual image data.
|
||||
std::cout << "Header size: " << completeHeader.size() << std::endl;
|
||||
std::cout << "First frame size: " << frames[0].data.size() << std::endl;
|
||||
|
||||
writeGif("unchanged.gif", completeHeader, frames, loopExtension);
|
||||
|
||||
// reverse the frames
|
||||
std::vector<GifFrame> reversedFrames = reverseFrames(frames);
|
||||
|
||||
std::cout << "Comparing original first frame vs. last reversed frame...\n";
|
||||
|
||||
std::cout << "Original first frame size: " << frames[0].data.size() << "\n";
|
||||
std::cout << "Reversed last frame size: " << reversedFrames.back().data.size() << "\n";
|
||||
|
||||
/*
|
||||
std::cout << "Original first frame data:\n";
|
||||
for (size_t i = 0; i < frames[0].data.size(); ++i) {
|
||||
std::cout << std::hex << (int)frames[0].data[i] << " ";
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
|
||||
std::cout << "Reversed last frame data:\n";
|
||||
for (size_t i = 0; i < reversedFrames.back().data.size(); ++i) {
|
||||
std::cout << std::hex << (int)reversedFrames.back().data[i] << " ";
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
*/
|
||||
// compare first frame of original vs last frame of reversed
|
||||
if (!frames.empty() && !reversedFrames.empty()) {
|
||||
std::cout << "First frame of original GIF (hex): ";
|
||||
for (size_t i = 0; i < std::min(frames[0].data.size(), (size_t)100); ++i) {
|
||||
std::cout << std::hex << (int)frames[0].data[i] << " ";
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
std::cout << "Last frame of reversed GIF (hex): ";
|
||||
for (size_t i = 0; i < std::min(reversedFrames.back().data.size(), (size_t)100); ++i) {
|
||||
std::cout << std::hex << (int)reversedFrames.back().data[i] << " ";
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
}
|
||||
|
||||
if (!frames.empty() && !reversedFrames.empty()) {
|
||||
//std::cout << "Last frame of original GIF (hex): ";
|
||||
//for (size_t i = 0; i < (frames.back()).data.size(); ++i) {
|
||||
// for (size_t i = 0; i < std::min((frames.back()).data.size(), (size_t)100); ++i) {
|
||||
// std::cout << std::hex << (int)(frames.back()).data[i] << " ";
|
||||
//}
|
||||
//std::cout << std::dec << std::endl;
|
||||
//std::cout << "First frame of reversed GIF (hex): ";
|
||||
//for (size_t i = 0; i < reversedFrames[0].data.size(); ++i) {
|
||||
// for (size_t i = 0; i < std::min(reversedFrames[0].data.size(), (size_t)100); ++i) {
|
||||
// std::cout << std::hex << (int)reversedFrames[0].data[i] << " ";
|
||||
//}
|
||||
//std::cout << std::dec << std::endl;
|
||||
}
|
||||
std::cout << "Original First Frame Disposal: " << (int)frames[0].disposalMethod << std::endl;
|
||||
std::cout << "Reversed Last Frame Disposal: " << (int)reversedFrames.back().disposalMethod << std::endl;
|
||||
|
||||
// write out the new GIF with the complete header
|
||||
writeGif("reversed.gif", completeHeader, reversedFrames, loopExtension);
|
||||
|
||||
289
labs/list_iterators_new/main_cp1.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
class GifFrame {
|
||||
public:
|
||||
std::vector<uint8_t> data; // raw image data
|
||||
int width, height; // frame dimensions
|
||||
int left, top; // position within the GIF
|
||||
uint8_t disposalMethod = 0; // add this field (0 = undefined, 1 = keep, etc.)
|
||||
bool hasTransparency = false; // flag for transparency
|
||||
uint8_t transparentColorIndex = 0; // index of the transparent color in the global color table
|
||||
uint16_t delayTime = 0; // delay time before the next frame in hundredths of a second
|
||||
// default constructor
|
||||
GifFrame() : width(0), height(0), left(0), top(0), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
// constructor to initialize the frame data
|
||||
GifFrame(const std::vector<uint8_t>& frameData) : data(frameData), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> loopExtension; // Stores Netscape Loop Extension
|
||||
|
||||
void printPalette(const std::vector<uint8_t>& fileData, size_t pos, int paletteSize) {
|
||||
// Print the first 3 bytes (RGB values) for each color in the palette
|
||||
for (int i = 0; i < paletteSize; i++) {
|
||||
uint8_t r = fileData[pos + i * 3]; // Red
|
||||
uint8_t g = fileData[pos + i * 3 + 1]; // Green
|
||||
uint8_t b = fileData[pos + i * 3 + 2]; // Blue
|
||||
// std::cout << "Color " << i << ": (" << (int)r << ", " << (int)g << ", " << (int)b << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void printFrameIndices(const std::vector<uint8_t>& frameData) {
|
||||
std::cout << "Frame Indices (first 20 bytes): ";
|
||||
for (size_t i = 0; i < std::min(frameData.size(), size_t(20)); ++i) {
|
||||
std::cout << (int)frameData[i] << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void printFullFrameData(const std::vector<uint8_t>& frameData) {
|
||||
std::cout << "Frame Data: ";
|
||||
for (size_t i = 0; i < frameData.size(); i++) {
|
||||
std::cout << (int)frameData[i] << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
// Helper function to extract the complete header from the GIF file data.
|
||||
std::vector<uint8_t> extractGifHeader(const std::vector<uint8_t>& fileData) {
|
||||
// Read first 6 bytes (signature)
|
||||
std::vector<uint8_t> header(fileData.begin(), fileData.begin() + 6);
|
||||
// Read next 7 bytes (logical screen descriptor)
|
||||
header.insert(header.end(), fileData.begin() + 6, fileData.begin() + 13);
|
||||
|
||||
// Check for Global Color Table flag (bit 7 of the LSD's 7th byte)
|
||||
uint8_t packedFields = fileData[10]; // This is in the LSD (byte 11 in 1-indexed terms)
|
||||
if (packedFields & 0x80) {
|
||||
// The size of the GCT is 3 * 2^(N+1) where N is in the lower 3 bits of the packed field.
|
||||
int sizeOfGCT = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
header.insert(header.end(), fileData.begin() + 13, fileData.begin() + 13 + sizeOfGCT);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
// check point 1: complete this function, so as to reverse vector without using iterators.
|
||||
void reverseFrames(std::vector<GifFrame>& frames) {
|
||||
}
|
||||
|
||||
void writeGif(const std::string& outputFilename, const std::vector<uint8_t>& originalFileData,
|
||||
const std::vector<GifFrame>& frames, const std::vector<uint8_t>& loopExtension) {
|
||||
if (frames.empty()) {
|
||||
std::cerr << "No frames to write!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream outFile(outputFilename, std::ios::binary);
|
||||
if (!outFile) {
|
||||
std::cerr << "Error creating output file!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// The first 13 bytes of the GIF header are typically static (GIF signature, version, and logical screen descriptor).
|
||||
outFile.write(reinterpret_cast<const char*>(originalFileData.data()), 13);
|
||||
|
||||
size_t pos = 13;
|
||||
uint8_t packedFields = originalFileData[10];
|
||||
|
||||
// if a global color table exists.
|
||||
if (packedFields & 0x80) {
|
||||
// extracts the size of the global color table:
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
outFile.write(reinterpret_cast<const char*>(&originalFileData[pos]), gctSize);
|
||||
pos += gctSize;
|
||||
}
|
||||
|
||||
if (!loopExtension.empty()) {
|
||||
outFile.write(reinterpret_cast<const char*>(loopExtension.data()), loopExtension.size());
|
||||
}
|
||||
|
||||
for (const auto& frame : frames) {
|
||||
outFile.write(reinterpret_cast<const char*>(frame.data.data()), frame.data.size());
|
||||
}
|
||||
|
||||
uint8_t trailer = 0x3B;
|
||||
outFile.write(reinterpret_cast<const char*>(&trailer), 1);
|
||||
|
||||
outFile.close();
|
||||
std::cout << "GIF successfully written to " << outputFilename << "\n";
|
||||
}
|
||||
|
||||
std::vector<GifFrame> extractFrames(const std::string& filename, std::vector<uint8_t>& fileData, std::vector<uint8_t>& loopExtension) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error opening file!\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
fileData = std::vector<uint8_t>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
if (fileData.size() < 6 || fileData[0] != 'G' || fileData[1] != 'I' || fileData[2] != 'F') {
|
||||
std::cerr << "Not a valid GIF file!\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<GifFrame> frames;
|
||||
size_t pos = 13;
|
||||
|
||||
// GIF Header Structure: all GIF files must start with a header block. The header takes up the first six bytes of the file.
|
||||
// The first 13 bytes of a standard GIF file are fixed and contain the following data:
|
||||
// Bytes 0-2: The signature "GIF" (3 bytes)
|
||||
// Bytes 3-5: The version, typically "87a" or "89a" (3 bytes)
|
||||
// Bytes 6-7: Logical screen width (2 bytes)
|
||||
// Bytes 8-9: Logical screen height (2 bytes)
|
||||
// Byte 10: Packed fields (1 byte)
|
||||
// Byte 11: Background color index (1 byte)
|
||||
// Byte 12: Pixel aspect ratio (1 byte)
|
||||
// Byte 10 is packed fields, which has 8 bits:
|
||||
// Bit 7: The first (most-significant) bit is the global color table flag.
|
||||
// If it's 0, then there is no global color table. If it's 1, then a global color table will follow. i
|
||||
// Bit 6-4: The next three bits are the color resolution.
|
||||
// They are only meaningful if there is a global color table, and allow you to compute its size.
|
||||
// If the value of this field is N, the number of entries in the global color table will be 2 ^ (N+1) - that is, two raised to the power (N+1).
|
||||
// Bit 3: The next single bit is the sort flag.
|
||||
// If the values is 1, then the colors in the global color table are sorted in order of "decreasing importance," which typically means "decreasing frequency" in the image.
|
||||
// This can help the image decoder, but is not required.
|
||||
// Bit 2-0: Size of global color table.
|
||||
uint8_t packedFields = fileData[10];
|
||||
// if GCT Flag is set (GCT is present).
|
||||
if (packedFields & 0x80) {
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
std::cout << "Global Color Table Size: " << gctSize << " bytes\n";
|
||||
printPalette(fileData, 13, gctSize); // Starting at position 13 (after the logical screen descriptor)
|
||||
pos += gctSize;
|
||||
}
|
||||
|
||||
// after the 13 bytes, we have graphic control extension blocks, which are used to specify transparency settings and control animations.
|
||||
std::vector<uint8_t> gceBlock;
|
||||
|
||||
while (pos < fileData.size()) {
|
||||
if (fileData[pos] == 0x21) { // graphics control extension block, all extension blocks begin with 21. It is 8 bytes long and contains seven fields.
|
||||
// this is followed by an extension type byte, which indicates the type of extension.
|
||||
// the codes for the Graphics Control and Application extensions are 0xF9 and 0xFF, respectively.
|
||||
// the GIF89a specification adds four extension blocks to the original GIF87a format.
|
||||
// the four extension blocks are the Graphics Control, Application, Comment, and Plain Text Extensions.
|
||||
if (fileData[pos + 1] == 0xF9) { // next is the graphic control label, F9, which is the value that flags this as a graphic control extension.
|
||||
// third up is the total block size in bytes. This is always 0x04, because there are four more bytes of information before the extension block terminator.
|
||||
// next is a packed field. bits 5-7 are reserved for future use.
|
||||
// bits 2-4 indicate disposal method.
|
||||
// bit 1 is the user input flag.
|
||||
// bit 0 is the transparent color flag.
|
||||
gceBlock.assign(fileData.begin() + pos, fileData.begin() + pos + 8);
|
||||
pos += 8;
|
||||
// extract the disposal method from the packed field in the GCE
|
||||
uint8_t packedField = gceBlock[3]; // This is the packed field in the GCE
|
||||
uint8_t disposalMethod = (packedField >> 2) & 0b00000111; // extract the disposal method, i.e., bit 2-4.
|
||||
// std::cout << "Disposal Method: " << (int)disposalMethod << std::endl;
|
||||
// extract transparent color flag (bit 0)
|
||||
uint8_t transparentColorFlag = packedField & 0b00000001;
|
||||
// std::cout << "Transparent Color Flag: " << (int)transparentColorFlag << std::endl;
|
||||
|
||||
// extract delay time (2 bytes, little-endian)
|
||||
uint16_t delayTime = (gceBlock[4] | (gceBlock[5] << 8));
|
||||
// std::cout << "Delay Time: " << delayTime << " hundredths of a second" << std::endl;
|
||||
|
||||
// extract transparent color index (if transparent color flag is set)
|
||||
uint8_t transparentColorIndex = 0; // Default value if no transparency
|
||||
if (transparentColorFlag == 1) {
|
||||
transparentColorIndex = gceBlock[6];
|
||||
// std::cout << "Transparent Color Index: " << (int)transparentColorIndex << std::endl;
|
||||
}
|
||||
|
||||
// store the extracted information in the GifFrame structure
|
||||
if (!frames.empty()) {
|
||||
GifFrame& currentFrame = frames.back(); // store in the last frame added
|
||||
currentFrame.disposalMethod = disposalMethod;
|
||||
currentFrame.hasTransparency = transparentColorFlag;
|
||||
currentFrame.delayTime = delayTime;
|
||||
currentFrame.transparentColorIndex = transparentColorIndex;
|
||||
}
|
||||
} else if (fileData[pos + 1] == 0xFF) { // the application extension block allows GIF files to be customized for particular applications. Netscape took advantage of this feature to supplement the graphics control block with an application extension block that would tell a web browser how many times to display a sequence of images in an animated GIF before stopping.
|
||||
// Netscape Loop Extension
|
||||
size_t loopStart = pos;
|
||||
pos += 2; // Move past 0x21 FF
|
||||
|
||||
if (pos + 11 < fileData.size() && fileData[pos] == 0x0B &&
|
||||
std::equal(fileData.begin() + pos + 1, fileData.begin() + pos + 12, "NETSCAPE2.0")) {
|
||||
|
||||
pos += 12; // Move past "NETSCAPE2.0"
|
||||
// Extract the entire block starting from 0x21
|
||||
std::vector<uint8_t> extensionData;
|
||||
// Go back to the start of the extension block
|
||||
extensionData.insert(extensionData.end(), fileData.begin() + loopStart, fileData.begin() + pos);
|
||||
|
||||
// Read sub-blocks
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) {
|
||||
size_t blockSize = fileData[pos]; // Get block size
|
||||
extensionData.insert(extensionData.end(), fileData.begin() + pos, fileData.begin() + pos + blockSize + 1);
|
||||
pos += blockSize + 1;
|
||||
}
|
||||
|
||||
// Include the block terminator (0x00)
|
||||
if (pos < fileData.size() && fileData[pos] == 0x00) {
|
||||
extensionData.push_back(0x00);
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Store the full extension data
|
||||
loopExtension.assign(extensionData.begin(), extensionData.end());
|
||||
}
|
||||
} else { // Other extension blocks
|
||||
pos += 2;
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) {
|
||||
pos += fileData[pos] + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
} else if (fileData[pos] == 0x2C) { // image descriptor. The image descriptor always consists of 10 bytes that contain the dimensions of the image and information on which color table to use and how the image data is stored.
|
||||
// Every image descriptor begins with the value 2C. The next 8 bytes represent the location and size of the following image. The last byte is another packed field.
|
||||
size_t frameStart = pos;
|
||||
pos += 10;
|
||||
if (fileData[frameStart + 9] & 0x80) { // check the packed field. This bit is set to 1 if the Image Block contains a Local Color Table that should be used when rendering the image.
|
||||
int lctSize = 3 * (1 << ((fileData[frameStart + 9] & 0x07) + 1));
|
||||
pos += lctSize;
|
||||
}
|
||||
pos++;
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) { // 0 means that no bytes follow and we have read all the data in this block.
|
||||
pos += fileData[pos] + 1;
|
||||
}
|
||||
pos++;
|
||||
std::vector<uint8_t> frameData;
|
||||
if (!gceBlock.empty()) {
|
||||
// the insert function insert the bytes from gceBlock to the beginning of frameData.
|
||||
frameData.insert(frameData.begin(), gceBlock.begin(), gceBlock.end());
|
||||
}
|
||||
// appends the bytes starting from frameStart to the current position (pos), which corresponds to the image data for the frame.
|
||||
frameData.insert(frameData.end(), fileData.begin() + frameStart, fileData.begin() + pos);
|
||||
// std::cout << "Extracted frame size: " << frameData.size() << " bytes\n";
|
||||
// std::cout << "Last byte of this frame is: " << (int)(frameData.back()) << "\n";
|
||||
frames.push_back(GifFrame(frameData));
|
||||
} else if (fileData[pos] == 0x3B) {
|
||||
// A GIF file always starts with the three-byte signature “GIF” and ends with the byte (in hex) “3B,” which indicates the end of the data stream.
|
||||
break;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// read the complete file into fileData, and extract frames.
|
||||
std::vector<uint8_t> fileData;
|
||||
std::vector<GifFrame> frames = extractFrames("input.gif", fileData, loopExtension);
|
||||
if (frames.empty()) return 1;
|
||||
|
||||
// extract the complete header from fileData
|
||||
std::vector<uint8_t> completeHeader = extractGifHeader(fileData);
|
||||
|
||||
// reverse the frames
|
||||
reverseFrames(frames);
|
||||
|
||||
// write out the new GIF with the complete header
|
||||
writeGif("reversed.gif", completeHeader, frames, loopExtension);
|
||||
|
||||
return 0;
|
||||
}
|
||||
296
labs/list_iterators_new/main_cp2.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class GifFrame {
|
||||
public:
|
||||
std::vector<uint8_t> data; // raw image data
|
||||
int width, height; // frame dimensions
|
||||
int left, top; // position within the GIF
|
||||
uint8_t disposalMethod = 0; // add this field (0 = undefined, 1 = keep, etc.)
|
||||
bool hasTransparency = false; // flag for transparency
|
||||
uint8_t transparentColorIndex = 0; // index of the transparent color in the global color table
|
||||
uint16_t delayTime = 0; // delay time before the next frame in hundredths of a second
|
||||
// default constructor
|
||||
GifFrame() : width(0), height(0), left(0), top(0), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
// constructor to initialize the frame data
|
||||
GifFrame(const std::vector<uint8_t>& frameData) : data(frameData), disposalMethod(0),
|
||||
hasTransparency(false), transparentColorIndex(0), delayTime(0) {}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> loopExtension; // Stores Netscape Loop Extension
|
||||
|
||||
void printPalette(const std::vector<uint8_t>& fileData, size_t pos, int paletteSize) {
|
||||
// Print the first 3 bytes (RGB values) for each color in the palette
|
||||
for (int i = 0; i < paletteSize; i++) {
|
||||
uint8_t r = fileData[pos + i * 3]; // Red
|
||||
uint8_t g = fileData[pos + i * 3 + 1]; // Green
|
||||
uint8_t b = fileData[pos + i * 3 + 2]; // Blue
|
||||
// std::cout << "Color " << i << ": (" << (int)r << ", " << (int)g << ", " << (int)b << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void printFrameIndices(const std::vector<uint8_t>& frameData) {
|
||||
std::cout << "Frame Indices (first 20 bytes): ";
|
||||
for (size_t i = 0; i < std::min(frameData.size(), size_t(20)); ++i) {
|
||||
std::cout << (int)frameData[i] << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void printFullFrameData(const std::vector<uint8_t>& frameData) {
|
||||
std::cout << "Frame Data: ";
|
||||
for (size_t i = 0; i < frameData.size(); i++) {
|
||||
std::cout << (int)frameData[i] << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
// Helper function to extract the complete header from the GIF file data.
|
||||
std::vector<uint8_t> extractGifHeader(const std::vector<uint8_t>& fileData) {
|
||||
// Read first 6 bytes (signature)
|
||||
std::vector<uint8_t> header(fileData.begin(), fileData.begin() + 6);
|
||||
// Read next 7 bytes (logical screen descriptor)
|
||||
header.insert(header.end(), fileData.begin() + 6, fileData.begin() + 13);
|
||||
|
||||
// Check for Global Color Table flag (bit 7 of the LSD's 7th byte)
|
||||
uint8_t packedFields = fileData[10]; // This is in the LSD (byte 11 in 1-indexed terms)
|
||||
if (packedFields & 0x80) {
|
||||
// The size of the GCT is 3 * 2^(N+1) where N is in the lower 3 bits of the packed field.
|
||||
int sizeOfGCT = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
header.insert(header.end(), fileData.begin() + 13, fileData.begin() + 13 + sizeOfGCT);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
// check point 2: complete this function, so as to reverse this std::list.
|
||||
void reverseFrames(std::list<GifFrame>& frames) {
|
||||
}
|
||||
|
||||
void writeGif(const std::string& outputFilename, const std::vector<uint8_t>& originalFileData,
|
||||
const std::vector<GifFrame>& frames, const std::vector<uint8_t>& loopExtension) {
|
||||
if (frames.empty()) {
|
||||
std::cerr << "No frames to write!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream outFile(outputFilename, std::ios::binary);
|
||||
if (!outFile) {
|
||||
std::cerr << "Error creating output file!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// The first 13 bytes of the GIF header are typically static (GIF signature, version, and logical screen descriptor).
|
||||
outFile.write(reinterpret_cast<const char*>(originalFileData.data()), 13);
|
||||
|
||||
size_t pos = 13;
|
||||
uint8_t packedFields = originalFileData[10];
|
||||
|
||||
// if a global color table exists.
|
||||
if (packedFields & 0x80) {
|
||||
// extracts the size of the global color table:
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
outFile.write(reinterpret_cast<const char*>(&originalFileData[pos]), gctSize);
|
||||
pos += gctSize;
|
||||
}
|
||||
|
||||
if (!loopExtension.empty()) {
|
||||
outFile.write(reinterpret_cast<const char*>(loopExtension.data()), loopExtension.size());
|
||||
}
|
||||
|
||||
for (const auto& frame : frames) {
|
||||
outFile.write(reinterpret_cast<const char*>(frame.data.data()), frame.data.size());
|
||||
}
|
||||
|
||||
uint8_t trailer = 0x3B;
|
||||
outFile.write(reinterpret_cast<const char*>(&trailer), 1);
|
||||
|
||||
outFile.close();
|
||||
std::cout << "GIF successfully written to " << outputFilename << "\n";
|
||||
}
|
||||
|
||||
std::vector<GifFrame> extractFrames(const std::string& filename, std::vector<uint8_t>& fileData, std::vector<uint8_t>& loopExtension) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error opening file!\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
fileData = std::vector<uint8_t>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
if (fileData.size() < 6 || fileData[0] != 'G' || fileData[1] != 'I' || fileData[2] != 'F') {
|
||||
std::cerr << "Not a valid GIF file!\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<GifFrame> frames;
|
||||
size_t pos = 13;
|
||||
|
||||
// GIF Header Structure: all GIF files must start with a header block. The header takes up the first six bytes of the file.
|
||||
// The first 13 bytes of a standard GIF file are fixed and contain the following data:
|
||||
// Bytes 0-2: The signature "GIF" (3 bytes)
|
||||
// Bytes 3-5: The version, typically "87a" or "89a" (3 bytes)
|
||||
// Bytes 6-7: Logical screen width (2 bytes)
|
||||
// Bytes 8-9: Logical screen height (2 bytes)
|
||||
// Byte 10: Packed fields (1 byte)
|
||||
// Byte 11: Background color index (1 byte)
|
||||
// Byte 12: Pixel aspect ratio (1 byte)
|
||||
// Byte 10 is packed fields, which has 8 bits:
|
||||
// Bit 7: The first (most-significant) bit is the global color table flag.
|
||||
// If it's 0, then there is no global color table. If it's 1, then a global color table will follow. i
|
||||
// Bit 6-4: The next three bits are the color resolution.
|
||||
// They are only meaningful if there is a global color table, and allow you to compute its size.
|
||||
// If the value of this field is N, the number of entries in the global color table will be 2 ^ (N+1) - that is, two raised to the power (N+1).
|
||||
// Bit 3: The next single bit is the sort flag.
|
||||
// If the values is 1, then the colors in the global color table are sorted in order of "decreasing importance," which typically means "decreasing frequency" in the image.
|
||||
// This can help the image decoder, but is not required.
|
||||
// Bit 2-0: Size of global color table.
|
||||
uint8_t packedFields = fileData[10];
|
||||
// if GCT Flag is set (GCT is present).
|
||||
if (packedFields & 0x80) {
|
||||
int gctSize = 3 * (1 << ((packedFields & 0x07) + 1));
|
||||
std::cout << "Global Color Table Size: " << gctSize << " bytes\n";
|
||||
printPalette(fileData, 13, gctSize); // Starting at position 13 (after the logical screen descriptor)
|
||||
pos += gctSize;
|
||||
}
|
||||
|
||||
// after the 13 bytes, we have graphic control extension blocks, which are used to specify transparency settings and control animations.
|
||||
std::vector<uint8_t> gceBlock;
|
||||
|
||||
while (pos < fileData.size()) {
|
||||
if (fileData[pos] == 0x21) { // graphics control extension block, all extension blocks begin with 21. It is 8 bytes long and contains seven fields.
|
||||
// this is followed by an extension type byte, which indicates the type of extension.
|
||||
// the codes for the Graphics Control and Application extensions are 0xF9 and 0xFF, respectively.
|
||||
// the GIF89a specification adds four extension blocks to the original GIF87a format.
|
||||
// the four extension blocks are the Graphics Control, Application, Comment, and Plain Text Extensions.
|
||||
if (fileData[pos + 1] == 0xF9) { // next is the graphic control label, F9, which is the value that flags this as a graphic control extension.
|
||||
// third up is the total block size in bytes. This is always 0x04, because there are four more bytes of information before the extension block terminator.
|
||||
// next is a packed field. bits 5-7 are reserved for future use.
|
||||
// bits 2-4 indicate disposal method.
|
||||
// bit 1 is the user input flag.
|
||||
// bit 0 is the transparent color flag.
|
||||
gceBlock.assign(fileData.begin() + pos, fileData.begin() + pos + 8);
|
||||
pos += 8;
|
||||
// extract the disposal method from the packed field in the GCE
|
||||
uint8_t packedField = gceBlock[3]; // This is the packed field in the GCE
|
||||
uint8_t disposalMethod = (packedField >> 2) & 0b00000111; // extract the disposal method, i.e., bit 2-4.
|
||||
// std::cout << "Disposal Method: " << (int)disposalMethod << std::endl;
|
||||
// extract transparent color flag (bit 0)
|
||||
uint8_t transparentColorFlag = packedField & 0b00000001;
|
||||
// std::cout << "Transparent Color Flag: " << (int)transparentColorFlag << std::endl;
|
||||
|
||||
// extract delay time (2 bytes, little-endian)
|
||||
uint16_t delayTime = (gceBlock[4] | (gceBlock[5] << 8));
|
||||
// std::cout << "Delay Time: " << delayTime << " hundredths of a second" << std::endl;
|
||||
|
||||
// extract transparent color index (if transparent color flag is set)
|
||||
uint8_t transparentColorIndex = 0; // Default value if no transparency
|
||||
if (transparentColorFlag == 1) {
|
||||
transparentColorIndex = gceBlock[6];
|
||||
// std::cout << "Transparent Color Index: " << (int)transparentColorIndex << std::endl;
|
||||
}
|
||||
|
||||
// store the extracted information in the GifFrame structure
|
||||
if (!frames.empty()) {
|
||||
GifFrame& currentFrame = frames.back(); // store in the last frame added
|
||||
currentFrame.disposalMethod = disposalMethod;
|
||||
currentFrame.hasTransparency = transparentColorFlag;
|
||||
currentFrame.delayTime = delayTime;
|
||||
currentFrame.transparentColorIndex = transparentColorIndex;
|
||||
}
|
||||
} else if (fileData[pos + 1] == 0xFF) { // the application extension block allows GIF files to be customized for particular applications. Netscape took advantage of this feature to supplement the graphics control block with an application extension block that would tell a web browser how many times to display a sequence of images in an animated GIF before stopping.
|
||||
// Netscape Loop Extension
|
||||
size_t loopStart = pos;
|
||||
pos += 2; // Move past 0x21 FF
|
||||
|
||||
if (pos + 11 < fileData.size() && fileData[pos] == 0x0B &&
|
||||
std::equal(fileData.begin() + pos + 1, fileData.begin() + pos + 12, "NETSCAPE2.0")) {
|
||||
|
||||
pos += 12; // Move past "NETSCAPE2.0"
|
||||
// Extract the entire block starting from 0x21
|
||||
std::vector<uint8_t> extensionData;
|
||||
// Go back to the start of the extension block
|
||||
extensionData.insert(extensionData.end(), fileData.begin() + loopStart, fileData.begin() + pos);
|
||||
|
||||
// Read sub-blocks
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) {
|
||||
size_t blockSize = fileData[pos]; // Get block size
|
||||
extensionData.insert(extensionData.end(), fileData.begin() + pos, fileData.begin() + pos + blockSize + 1);
|
||||
pos += blockSize + 1;
|
||||
}
|
||||
|
||||
// Include the block terminator (0x00)
|
||||
if (pos < fileData.size() && fileData[pos] == 0x00) {
|
||||
extensionData.push_back(0x00);
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Store the full extension data
|
||||
loopExtension.assign(extensionData.begin(), extensionData.end());
|
||||
}
|
||||
} else { // Other extension blocks
|
||||
pos += 2;
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) {
|
||||
pos += fileData[pos] + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
} else if (fileData[pos] == 0x2C) { // image descriptor. The image descriptor always consists of 10 bytes that contain the dimensions of the image and information on which color table to use and how the image data is stored.
|
||||
// Every image descriptor begins with the value 2C. The next 8 bytes represent the location and size of the following image. The last byte is another packed field.
|
||||
size_t frameStart = pos;
|
||||
pos += 10;
|
||||
if (fileData[frameStart + 9] & 0x80) { // check the packed field. This bit is set to 1 if the Image Block contains a Local Color Table that should be used when rendering the image.
|
||||
int lctSize = 3 * (1 << ((fileData[frameStart + 9] & 0x07) + 1));
|
||||
pos += lctSize;
|
||||
}
|
||||
pos++;
|
||||
while (pos < fileData.size() && fileData[pos] != 0x00) { // 0 means that no bytes follow and we have read all the data in this block.
|
||||
pos += fileData[pos] + 1;
|
||||
}
|
||||
pos++;
|
||||
std::vector<uint8_t> frameData;
|
||||
if (!gceBlock.empty()) {
|
||||
// the insert function insert the bytes from gceBlock to the beginning of frameData.
|
||||
frameData.insert(frameData.begin(), gceBlock.begin(), gceBlock.end());
|
||||
}
|
||||
// appends the bytes starting from frameStart to the current position (pos), which corresponds to the image data for the frame.
|
||||
frameData.insert(frameData.end(), fileData.begin() + frameStart, fileData.begin() + pos);
|
||||
// std::cout << "Extracted frame size: " << frameData.size() << " bytes\n";
|
||||
// std::cout << "Last byte of this frame is: " << (int)(frameData.back()) << "\n";
|
||||
frames.push_back(GifFrame(frameData));
|
||||
} else if (fileData[pos] == 0x3B) {
|
||||
// A GIF file always starts with the three-byte signature “GIF” and ends with the byte (in hex) “3B,” which indicates the end of the data stream.
|
||||
break;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// read the complete file into fileData, and extract frames.
|
||||
std::vector<uint8_t> fileData;
|
||||
std::vector<GifFrame> frames = extractFrames("input.gif", fileData, loopExtension);
|
||||
if (frames.empty()) return 1;
|
||||
|
||||
// extract the complete header from fileData
|
||||
std::vector<uint8_t> completeHeader = extractGifHeader(fileData);
|
||||
|
||||
// convert the vector to list
|
||||
std::list<GifFrame> framesList(frames.begin(), frames.end());
|
||||
|
||||
// reverse the frames
|
||||
reverseFrames(framesList);
|
||||
|
||||
// convert the list to vector
|
||||
std::vector<GifFrame> reversedFrames(framesList.begin(), framesList.end());
|
||||
|
||||
// write out the new GIF with the complete header
|
||||
writeGif("reversed.gif", completeHeader, reversedFrames, loopExtension);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
labs/list_iterators_new/pool_fake.gif
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
labs/list_iterators_new/pool_real.gif
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
18
labs/list_iterators_new/reverse_iterator.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
int main(){
|
||||
|
||||
std::list<int> a;
|
||||
unsigned int i;
|
||||
for ( i=1; i<10; ++i ){
|
||||
a.push_back( i*i );
|
||||
}
|
||||
|
||||
std::list<int>::reverse_iterator ri;
|
||||
for( ri = a.rbegin(); ri != a.rend(); ++ri ){
|
||||
std::cout << *ri << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
labs/list_iterators_new/seat_fake.gif
Normal file
|
After Width: | Height: | Size: 859 KiB |
BIN
labs/list_iterators_new/seat_real.gif
Normal file
|
After Width: | Height: | Size: 859 KiB |
|
Before Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 919 KiB |
|
Before Width: | Height: | Size: 15 MiB |
|
Before Width: | Height: | Size: 15 MiB |