2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
30 Image
juce_loadWithCoreImage (InputStream
& input
);
33 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6385)
35 //==============================================================================
39 GIFLoader (InputStream
& in
)
41 dataBlockIsZero (false), fresh (false), finished (false),
42 currentBit (0), lastBit (0), lastByteIndex (0),
43 codeSize (0), setCodeSize (0), maxCode (0), maxCodeSize (0),
44 firstcode (0), oldcode (0), clearCode (0), endCode (0)
46 int imageWidth
, imageHeight
;
47 if (! getSizeFromHeader (imageWidth
, imageHeight
))
51 if (in
.read (buf
, 3) != 3)
54 int numColours
= 2 << (buf
[0] & 7);
57 if ((buf
[0] & 0x80) != 0)
58 readPalette (numColours
);
62 if (input
.read (buf
, 1) != 1 || buf
[0] == ';')
67 if (readExtension (transparent
))
76 if (input
.read (buf
, 9) == 9)
78 imageWidth
= (int) ByteOrder::littleEndianShort (buf
+ 4);
79 imageHeight
= (int) ByteOrder::littleEndianShort (buf
+ 6);
81 numColours
= 2 << (buf
[8] & 7);
83 if ((buf
[8] & 0x80) != 0)
84 if (! readPalette (numColours
))
87 image
= Image (transparent
>= 0 ? Image::ARGB
: Image::RGB
,
88 imageWidth
, imageHeight
, transparent
>= 0);
90 image
.getProperties()->set ("originalImageHadAlpha", transparent
>= 0);
92 readImage ((buf
[8] & 0x40) != 0, transparent
);
104 PixelARGB palette
[256];
105 bool dataBlockIsZero
, fresh
, finished
;
106 int currentBit
, lastBit
, lastByteIndex
;
107 int codeSize
, setCodeSize
;
108 int maxCode
, maxCodeSize
;
109 int firstcode
, oldcode
;
110 int clearCode
, endCode
;
111 enum { maxGifCode
= 1 << 12 };
112 int table
[2] [maxGifCode
];
113 int stack
[2 * maxGifCode
];
116 bool getSizeFromHeader (int& w
, int& h
)
118 // Add an extra byte for the zero terminator
121 if (input
.read (b
, 6) == 6
122 && (strncmp ("GIF87a", b
, 6) == 0
123 || strncmp ("GIF89a", b
, 6) == 0))
125 if (input
.read (b
, 4) == 4)
127 w
= (int) ByteOrder::littleEndianShort (b
);
128 h
= (int) ByteOrder::littleEndianShort (b
+ 2);
129 return w
> 0 && h
> 0;
136 bool readPalette (const int numCols
)
138 for (int i
= 0; i
< numCols
; ++i
)
143 palette
[i
].setARGB (0xff, rgb
[0], rgb
[1], rgb
[2]);
144 palette
[i
].premultiply();
150 int readDataBlock (uint8
* const dest
)
153 if (input
.read (&n
, 1) == 1)
155 dataBlockIsZero
= (n
== 0);
157 if (dataBlockIsZero
|| (input
.read (dest
, n
) == n
))
164 int readExtension (int& transparent
)
167 if (input
.read (&type
, 1) != 1)
175 n
= readDataBlock (b
);
185 n
= readDataBlock (b
);
195 for (i
= 0; i
< clearCode
; ++i
)
201 for (; i
< maxGifCode
; ++i
)
208 void initialise (const int inputCodeSize
)
210 setCodeSize
= inputCodeSize
;
211 codeSize
= setCodeSize
+ 1;
212 clearCode
= 1 << setCodeSize
;
213 endCode
= clearCode
+ 1;
214 maxCodeSize
= 2 * clearCode
;
215 maxCode
= clearCode
+ 2;
232 firstcode
= oldcode
= getCode (codeSize
, false);
234 if (firstcode
!= clearCode
)
244 while ((code
= getCode (codeSize
, false)) >= 0)
246 if (code
== clearCode
)
249 codeSize
= setCodeSize
+ 1;
250 maxCodeSize
= 2 * clearCode
;
251 maxCode
= clearCode
+ 2;
253 firstcode
= oldcode
= getCode (codeSize
, false);
256 else if (code
== endCode
)
264 while ((n
= readDataBlock (buf
)) > 0)
271 const int incode
= code
;
279 while (code
>= clearCode
)
281 *sp
++ = table
[1][code
];
282 if (code
== table
[0][code
])
285 code
= table
[0][code
];
288 *sp
++ = firstcode
= table
[1][code
];
290 if ((code
= maxCode
) < maxGifCode
)
292 table
[0][code
] = oldcode
;
293 table
[1][code
] = firstcode
;
296 if (maxCode
>= maxCodeSize
&& maxCodeSize
< maxGifCode
)
312 int getCode (const int codeSize_
, const bool shouldInitialise
)
314 if (shouldInitialise
)
322 if ((currentBit
+ codeSize_
) >= lastBit
)
327 buffer
[0] = buffer
[lastByteIndex
- 2];
328 buffer
[1] = buffer
[lastByteIndex
- 1];
330 const int n
= readDataBlock (buffer
+ 2);
335 lastByteIndex
= 2 + n
;
336 currentBit
= (currentBit
- lastBit
) + 16;
337 lastBit
= (2 + n
) * 8 ;
343 for (int j
= 0; j
< codeSize_
; ++j
)
345 result
|= ((buffer
[i
>> 3] & (1 << (i
& 7))) != 0) << j
;
349 currentBit
+= codeSize_
;
353 bool readImage (const int interlace
, const int transparent
)
356 if (input
.read (&c
, 1) != 1)
361 if (transparent
>= 0)
362 palette
[transparent
].setARGB (0, 0, 0, 0);
364 int xpos
= 0, ypos
= 0, yStep
= 8, pass
= 0;
366 const Image::BitmapData
destData (image
, Image::BitmapData::writeOnly
);
367 uint8
* p
= destData
.getPixelPointer (0, 0);
368 const bool hasAlpha
= image
.hasAlphaChannel();
372 const int index
= readLZWByte();
377 ((PixelARGB
*) p
)->set (palette
[index
]);
379 ((PixelRGB
*) p
)->set (palette
[index
]);
381 p
+= destData
.pixelStride
;
383 if (++xpos
== destData
.width
)
391 while (ypos
>= destData
.height
)
395 case 1: ypos
= 4; yStep
= 8; break;
396 case 2: ypos
= 2; yStep
= 4; break;
397 case 3: ypos
= 1; yStep
= 2; break;
398 default: return true;
404 if (++ypos
>= destData
.height
)
408 p
= destData
.getPixelPointer (xpos
, ypos
);
415 JUCE_DECLARE_NON_COPYABLE (GIFLoader
)
418 JUCE_END_IGNORE_WARNINGS_MSVC
422 //==============================================================================
423 GIFImageFormat::GIFImageFormat() {}
424 GIFImageFormat::~GIFImageFormat() {}
426 String
GIFImageFormat::getFormatName() { return "GIF"; }
427 bool GIFImageFormat::usesFileExtension (const File
& f
) { return f
.hasFileExtension ("gif"); }
429 bool GIFImageFormat::canUnderstand (InputStream
& in
)
433 return (in
.read (header
, sizeof (header
)) == (int) sizeof (header
))
439 Image
GIFImageFormat::decodeImage (InputStream
& in
)
441 #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
442 return juce_loadWithCoreImage (in
);
444 const std::unique_ptr
<GIFLoader
> loader (new GIFLoader (in
));
445 return loader
->image
;
449 bool GIFImageFormat::writeImageToStream (const Image
& /*sourceImage*/, OutputStream
& /*destStream*/)
451 jassertfalse
; // writing isn't implemented for GIFs!