diff --git a/src/savers/gif/gif.h b/src/savers/gif/gif.h index 16c577b5..32e1f409 100644 --- a/src/savers/gif/gif.h +++ b/src/savers/gif/gif.h @@ -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; iir[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; iir[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; } diff --git a/src/savers/gif/tvgGifSaver.cpp b/src/savers/gif/tvgGifSaver.cpp index 78924d3b..7d59489e 100644 --- a/src/savers/gif/tvgGifSaver.cpp +++ b/src/savers/gif/tvgGifSaver.cpp @@ -41,7 +41,7 @@ void GifSaver::run(unsigned tid) auto h = static_cast(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(buffer), w, h, uint32_t(delay * 100.0f))) { + if (!GifWriteFrame(&writer, reinterpret_cast(buffer), w, h, uint32_t(delay * 100.0f), transparent)) { TVGERR("GIF_SAVER", "Failed gif encoding"); break; } diff --git a/src/tools/lottie2gif/lottie2gif.cpp b/src/tools/lottie2gif/lottie2gif.cpp index d6f7c061..57df71af 100644 --- a/src/tools/lottie2gif/lottie2gif.cpp +++ b/src/tools/lottie2gif/lottie2gif.cpp @@ -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 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; }