mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
gif: support transparent gif animation
if no background is set, gif will generate transparent version. Issue: https://github.com/thorvg/thorvg/issues/1769
This commit is contained in:
parent
dfbb3893b0
commit
34f47671b1
3 changed files with 67 additions and 54 deletions
|
@ -279,7 +279,7 @@ void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, i
|
|||
}
|
||||
|
||||
// Finds all pixels that have changed from the previous image and
|
||||
// moves them to the fromt of th buffer.
|
||||
// moves them to the from of the buffer.
|
||||
// This allows us to build a palette optimized for the colors of the
|
||||
// changed pixels only.
|
||||
int GifPickChangedPixels( const uint8_t* lastFrame, uint8_t* frame, int numPixels )
|
||||
|
@ -289,10 +289,7 @@ int GifPickChangedPixels( const uint8_t* lastFrame, uint8_t* frame, int numPixel
|
|||
|
||||
for (int ii=0; ii<numPixels; ++ii)
|
||||
{
|
||||
if(lastFrame[0] != frame[0] ||
|
||||
lastFrame[1] != frame[1] ||
|
||||
lastFrame[2] != frame[2])
|
||||
{
|
||||
if ((frame[3] == 255) && (lastFrame[0] != frame[0] || lastFrame[1] != frame[1] || lastFrame[2] != frame[2])) {
|
||||
writeIter[0] = frame[0];
|
||||
writeIter[1] = frame[1];
|
||||
writeIter[2] = frame[2];
|
||||
|
@ -337,41 +334,55 @@ void GifMakePalette( const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_
|
|||
pPal->r[0] = pPal->g[0] = pPal->b[0] = 0;
|
||||
}
|
||||
|
||||
void GifPalettizePixel(const uint8_t* nextFrame, uint8_t* outFrame, GifPalette* pPal)
|
||||
{
|
||||
int32_t bestDiff = 1000000;
|
||||
int32_t bestInd = 1;
|
||||
GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], &bestInd, &bestDiff, 1);
|
||||
|
||||
// Write the resulting color to the output buffer
|
||||
outFrame[0] = pPal->r[bestInd];
|
||||
outFrame[1] = pPal->g[bestInd];
|
||||
outFrame[2] = pPal->b[bestInd];
|
||||
outFrame[3] = (uint8_t)bestInd;
|
||||
}
|
||||
|
||||
// Picks palette colors for the image using simple thresholding, no dithering
|
||||
void GifThresholdImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal )
|
||||
void GifThresholdImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal, bool transparent)
|
||||
{
|
||||
uint32_t numPixels = width*height;
|
||||
for( uint32_t ii=0; ii<numPixels; ++ii )
|
||||
{
|
||||
// if a previous color is available, and it matches the current color,
|
||||
// set the pixel to transparent
|
||||
if(lastFrame &&
|
||||
lastFrame[0] == nextFrame[0] &&
|
||||
lastFrame[1] == nextFrame[1] &&
|
||||
lastFrame[2] == nextFrame[2])
|
||||
{
|
||||
outFrame[0] = lastFrame[0];
|
||||
outFrame[1] = lastFrame[1];
|
||||
outFrame[2] = lastFrame[2];
|
||||
outFrame[3] = kGifTransIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// palettize the pixel
|
||||
int32_t bestDiff = 1000000;
|
||||
int32_t bestInd = 1;
|
||||
GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], &bestInd, &bestDiff, 1);
|
||||
|
||||
// Write the resulting color to the output buffer
|
||||
outFrame[0] = pPal->r[bestInd];
|
||||
outFrame[1] = pPal->g[bestInd];
|
||||
outFrame[2] = pPal->b[bestInd];
|
||||
outFrame[3] = (uint8_t)bestInd;
|
||||
if (transparent) {
|
||||
for (uint32_t ii = 0; ii < numPixels; ++ii) {
|
||||
if (nextFrame[3] < 255) {
|
||||
outFrame[0] = 0;
|
||||
outFrame[1] = 0;
|
||||
outFrame[2] = 0;
|
||||
outFrame[3] = kGifTransIndex;
|
||||
} else {
|
||||
GifPalettizePixel(nextFrame, outFrame, pPal);
|
||||
}
|
||||
if(lastFrame) lastFrame += 4;
|
||||
outFrame += 4;
|
||||
nextFrame += 4;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t ii = 0; ii < numPixels; ++ii) {
|
||||
// if a previous color is available, and it matches the current color,
|
||||
// set the pixel to transparent
|
||||
if( lastFrame && lastFrame[0] == nextFrame[0] && lastFrame[1] == nextFrame[1] && lastFrame[2] == nextFrame[2])
|
||||
{
|
||||
outFrame[0] = lastFrame[0];
|
||||
outFrame[1] = lastFrame[1];
|
||||
outFrame[2] = lastFrame[2];
|
||||
outFrame[3] = kGifTransIndex;
|
||||
} else {
|
||||
GifPalettizePixel(nextFrame, outFrame, pPal);
|
||||
}
|
||||
if(lastFrame) lastFrame += 4;
|
||||
outFrame += 4;
|
||||
nextFrame += 4;
|
||||
}
|
||||
|
||||
if(lastFrame) lastFrame += 4;
|
||||
outFrame += 4;
|
||||
nextFrame += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,13 +467,13 @@ void GifWritePalette( const GifPalette* pPal, FILE* f )
|
|||
}
|
||||
|
||||
// write the image header, LZW-compress and write out the image
|
||||
void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal)
|
||||
void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal, bool transparent)
|
||||
{
|
||||
// graphics control extension
|
||||
fputc(0x21, f);
|
||||
fputc(0xf9, f);
|
||||
fputc(0x04, f);
|
||||
fputc(0x05, f); // leave prev frame in place, this frame has transparency
|
||||
fputc((transparent ? 0x09 : 0x05), f); //clear prev frame or not.
|
||||
fputc(delay & 0xff, f);
|
||||
fputc((delay >> 8) & 0xff, f);
|
||||
fputc(kGifTransIndex, f); // transparent color index
|
||||
|
@ -644,7 +655,7 @@ bool GifBegin( GifWriter* writer, const char* filename, uint32_t width, uint32_t
|
|||
// The GIFWriter should have been created by GIFBegin.
|
||||
// AFAIK, it is legal to use different bit depths for different frames of an image -
|
||||
// this may be handy to save bits in animations that don't change much.
|
||||
bool GifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth = 8)
|
||||
bool GifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, bool transparent)
|
||||
{
|
||||
if(!writer->f) return false;
|
||||
|
||||
|
@ -652,11 +663,11 @@ bool GifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint
|
|||
writer->firstFrame = false;
|
||||
|
||||
GifPalette pal;
|
||||
GifMakePalette(oldImage, image, width, height, bitDepth, &pal);
|
||||
GifMakePalette(oldImage, image, width, height, 8, &pal);
|
||||
|
||||
GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal);
|
||||
GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal, transparent);
|
||||
|
||||
GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal);
|
||||
GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal, transparent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void GifSaver::run(unsigned tid)
|
|||
auto h = static_cast<uint32_t>(vsize[1]);
|
||||
|
||||
buffer = (uint32_t*)realloc(buffer, sizeof(uint32_t) * w * h);
|
||||
canvas->target(buffer, w, w, h, tvg::SwCanvas::ABGR8888);
|
||||
canvas->target(buffer, w, w, h, tvg::SwCanvas::ABGR8888S);
|
||||
canvas->push(cast(bg));
|
||||
canvas->push(cast(animation->picture()));
|
||||
|
||||
|
@ -52,6 +52,7 @@ void GifSaver::run(unsigned tid)
|
|||
}
|
||||
|
||||
auto delay = (1.0f / fps);
|
||||
auto transparent = bg ? false : true;
|
||||
|
||||
GifWriter writer;
|
||||
if (!GifBegin(&writer, path, w, h, uint32_t(delay * 100.f))) {
|
||||
|
@ -68,7 +69,7 @@ void GifSaver::run(unsigned tid)
|
|||
if (canvas->draw() == tvg::Result::Success) {
|
||||
canvas->sync();
|
||||
}
|
||||
if (!GifWriteFrame(&writer, reinterpret_cast<uint8_t*>(buffer), w, h, uint32_t(delay * 100.0f))) {
|
||||
if (!GifWriteFrame(&writer, reinterpret_cast<uint8_t*>(buffer), w, h, uint32_t(delay * 100.0f), transparent)) {
|
||||
TVGERR("GIF_SAVER", "Failed gif encoding");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ private:
|
|||
uint32_t fps = 30;
|
||||
uint32_t width = 600;
|
||||
uint32_t height = 600;
|
||||
uint32_t bgColor = 0xffffffff; //a white by default.
|
||||
unique_ptr<Shape> bg = nullptr; //transparent
|
||||
|
||||
void helpMsg()
|
||||
{
|
||||
|
@ -81,15 +81,10 @@ private:
|
|||
auto saver = Saver::gen();
|
||||
|
||||
//set a background color
|
||||
auto r = (uint8_t)((bgColor & 0xff0000) >> 16);
|
||||
auto g = (uint8_t)((bgColor & 0x00ff00) >> 8);
|
||||
auto b = (uint8_t)((bgColor & 0x0000ff));
|
||||
|
||||
auto bg = tvg::Shape::gen();
|
||||
bg->fill(r, g, b, 255);
|
||||
bg->appendRect(0, 0, width * scale, height * scale);
|
||||
saver->background(std::move(bg));
|
||||
|
||||
if (bg) {
|
||||
bg->appendRect(0, 0, width * scale, height * scale);
|
||||
saver->background(std::move(bg));
|
||||
}
|
||||
if (saver->save(std::move(animation), out, 100, fps) != Result::Success) return false;
|
||||
if (saver->sync() != Result::Success) return false;
|
||||
|
||||
|
@ -234,7 +229,13 @@ public:
|
|||
cout << "Error: Missing background color attribute. Expected eg. -b fa7410." << endl;
|
||||
return 1;
|
||||
}
|
||||
bgColor = (uint32_t) strtol(p_arg, NULL, 16);
|
||||
auto bgColor = (uint32_t) strtol(p_arg, NULL, 16);
|
||||
auto r = (uint8_t)((bgColor & 0xff0000) >> 16);
|
||||
auto g = (uint8_t)((bgColor & 0x00ff00) >> 8);
|
||||
auto b = (uint8_t)((bgColor & 0x0000ff));
|
||||
bg = tvg::Shape::gen();
|
||||
bg->fill(r, g, b, 255);
|
||||
|
||||
} else {
|
||||
cout << "Warning: Unknown flag (" << p << ")." << endl;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue