1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 // Functions to read images in GIF format. 17 18 #include "tensorflow/core/lib/gif/gif_io.h" 19 #include <algorithm> 20 #include "tensorflow/core/lib/gtl/cleanup.h" 21 #include "tensorflow/core/lib/strings/strcat.h" 22 #include "tensorflow/core/platform/gif.h" 23 #include "tensorflow/core/platform/logging.h" 24 #include "tensorflow/core/platform/mem.h" 25 #include "tensorflow/core/platform/types.h" 26 27 namespace tensorflow { 28 namespace gif { 29 30 struct InputBufferInfo { 31 const uint8_t* buf; 32 int bytes_left; 33 }; 34 35 int input_callback(GifFileType* gif_file, GifByteType* buf, int size) { 36 InputBufferInfo* const info = 37 reinterpret_cast<InputBufferInfo*>(gif_file->UserData); 38 if (info != nullptr) { 39 if (size > info->bytes_left) size = info->bytes_left; 40 memcpy(buf, info->buf, size); 41 info->buf += size; 42 info->bytes_left -= size; 43 return size; 44 } 45 return 0; 46 } 47 48 static const char* GifErrorStringNonNull(int error_code) { 49 const char* error_string = GifErrorString(error_code); 50 if (error_string == nullptr) { 51 return "Unknown error"; 52 } 53 return error_string; 54 } 55 56 uint8* Decode(const void* srcdata, int datasize, 57 const std::function<uint8*(int, int, int, int)>& allocate_output, 58 string* error_string) { 59 int error_code = D_GIF_SUCCEEDED; 60 InputBufferInfo info = {reinterpret_cast<const uint8*>(srcdata), datasize}; 61 GifFileType* gif_file = 62 DGifOpen(static_cast<void*>(&info), &input_callback, &error_code); 63 const auto cleanup = gtl::MakeCleanup([gif_file]() { 64 int error_code = D_GIF_SUCCEEDED; 65 if (gif_file && DGifCloseFile(gif_file, &error_code) != GIF_OK) { 66 LOG(WARNING) << "Fail to close gif file, reason: " 67 << GifErrorStringNonNull(error_code); 68 } 69 }); 70 if (error_code != D_GIF_SUCCEEDED) { 71 *error_string = strings::StrCat("failed to open gif file: ", 72 GifErrorStringNonNull(error_code)); 73 return nullptr; 74 } 75 if (DGifSlurp(gif_file) != GIF_OK) { 76 *error_string = strings::StrCat("failed to slurp gif file: ", 77 GifErrorStringNonNull(gif_file->Error)); 78 return nullptr; 79 } 80 if (gif_file->ImageCount <= 0) { 81 *error_string = strings::StrCat("gif file does not contain any image"); 82 return nullptr; 83 } 84 85 const int num_frames = gif_file->ImageCount; 86 const int width = gif_file->SWidth; 87 const int height = gif_file->SHeight; 88 const int channel = 3; 89 90 uint8* const dstdata = allocate_output(num_frames, width, height, channel); 91 if (!dstdata) return nullptr; 92 for (int k = 0; k < num_frames; k++) { 93 uint8* this_dst = dstdata + k * width * channel * height; 94 95 SavedImage* this_image = &gif_file->SavedImages[k]; 96 GifImageDesc* img_desc = &this_image->ImageDesc; 97 98 int imgLeft = img_desc->Left; 99 int imgTop = img_desc->Top; 100 int imgRight = img_desc->Left + img_desc->Width; 101 int imgBottom = img_desc->Top + img_desc->Height; 102 103 if (img_desc->Left != 0 || img_desc->Top != 0 || img_desc->Width != width || 104 img_desc->Height != height) { 105 // If the first frame does not fill the entire canvas then return error. 106 if (k == 0) { 107 *error_string = 108 strings::StrCat("the first frame does not fill the canvas"); 109 return nullptr; 110 } 111 // Otherwise previous frame will be reused to fill the unoccupied canvas. 112 imgLeft = std::max(imgLeft, 0); 113 imgTop = std::max(imgTop, 0); 114 imgRight = std::min(imgRight, width); 115 imgBottom = std::min(imgBottom, height); 116 117 uint8* last_dst = dstdata + (k - 1) * width * channel * height; 118 for (int i = 0; i < height; ++i) { 119 uint8* p_dst = this_dst + i * width * channel; 120 uint8* l_dst = last_dst + i * width * channel; 121 for (int j = 0; j < width; ++j) { 122 p_dst[j * channel + 0] = l_dst[j * channel + 0]; 123 p_dst[j * channel + 1] = l_dst[j * channel + 1]; 124 p_dst[j * channel + 2] = l_dst[j * channel + 2]; 125 } 126 } 127 } 128 129 ColorMapObject* color_map = this_image->ImageDesc.ColorMap 130 ? this_image->ImageDesc.ColorMap 131 : gif_file->SColorMap; 132 133 for (int i = imgTop; i < imgBottom; ++i) { 134 uint8* p_dst = this_dst + i * width * channel; 135 for (int j = imgLeft; j < imgRight; ++j) { 136 GifByteType color_index = 137 this_image->RasterBits[(i - img_desc->Top) * (img_desc->Width) + 138 (j - img_desc->Left)]; 139 const GifColorType& gif_color = color_map->Colors[color_index]; 140 p_dst[j * channel + 0] = gif_color.Red; 141 p_dst[j * channel + 1] = gif_color.Green; 142 p_dst[j * channel + 2] = gif_color.Blue; 143 } 144 } 145 } 146 147 return dstdata; 148 } 149 150 } // namespace gif 151 } // namespace tensorflow 152