4
Description : Contains all functions for the Tk extension for the CxImage utility
6
Author : Youness El Alaoui (KaKaRoTo - kakaroto@users.sourceforge.net)
11
static ChainedList animated_gifs;
12
Tk_ImageDisplayProc *PhotoDisplayOriginal=NULL;
14
/////////////////////////////////////
15
// Functions to manage lists //
16
/////////////////////////////////////
18
ChainedIterator TkCxImage_lstGetListItem(list_element_type list_element_id) { //Get the iterator with the specified id
22
for( item = g_list.begin(); item != g_list.end() && (*item)->list_element_id != list_element_id; item++);
29
struct data_item* TkCxImage_lstAddItem(struct data_item* item) { //Add the specified item if its id not already exists
31
if ( !item ) return NULL;
32
if ( TkCxImage_lstGetListItem(item->list_element_id) != g_list.end() ) return NULL;
34
g_list.push_back( item );
40
struct data_item* TkCxImage_lstGetItem(list_element_type list_element_id) { //Get the item with the specified id
42
ChainedIterator listitem = TkCxImage_lstGetListItem( list_element_id );
43
if( listitem != g_list.end() )
49
struct data_item* TkCxImage_lstDeleteItem(list_element_type list_element_id) { //Delete the item with the specified id if exists
51
ChainedIterator item = TkCxImage_lstGetListItem( list_element_id );
52
struct data_item* element;
54
if( item != g_list.end() ) {
65
////////////////////////////
67
////////////////////////////
69
int ChanMatch (Tcl_Channel chan, CONST char *fileName, Tcl_Obj *format,int *widthPtr,
70
int *heightPtr, Tcl_Interp *interp)
74
LOG("Chanel matching"); //
75
LOG("Filename is"); //
76
APPENDLOG(fileName); //
78
// Set escape to -1 to prevent decoding the image, but just return it's width and height
79
//image.SetEscape(-1);
81
if (image.Load(fileName, CXIMAGE_FORMAT_UNKNOWN)) {
82
*widthPtr = image.GetWidth();
83
*heightPtr = image.GetHeight();
85
LOG("Supported Format"); //
87
APPENDLOG(*widthPtr); //
89
APPENDLOG(*heightPtr); //
98
int ObjMatch (Tcl_Obj *data, Tcl_Obj *format, int *widthPtr, int *heightPtr, Tcl_Interp *interp) {
100
BYTE * buffer = NULL;
105
LOG("Data matching"); //
107
buffer = Tcl_GetByteArrayFromObj(data, &length);
111
if (image.Decode(buffer, length, CXIMAGE_FORMAT_GIF) ||
112
image.Decode(buffer, length, CXIMAGE_FORMAT_PNG) ||
113
image.Decode(buffer, length, CXIMAGE_FORMAT_JPG) ||
114
image.Decode(buffer, length, CXIMAGE_FORMAT_TGA) ||
115
image.Decode(buffer, length, CXIMAGE_FORMAT_BMP)) {
116
*widthPtr = image.GetWidth();
117
*heightPtr = image.GetHeight();
119
LOG("Supported Format"); //
121
APPENDLOG(*widthPtr); //
123
APPENDLOG(*heightPtr); //
128
LOG("Unknown format");
133
int ChanRead (Tcl_Interp *interp, Tcl_Channel chan, CONST char *fileName, Tcl_Obj *format, Tk_PhotoHandle imageHandle,
134
int destX, int destY, int width, int height, int srcX, int srcY)
136
Tcl_Obj *data = Tcl_NewObj();
138
Tcl_SetChannelOption(interp, chan, "-encoding", "binary");
139
Tcl_SetChannelOption(interp, chan, "-translation", "binary");
141
Tcl_ReadChars(chan, data, -1, 0);
143
LOG("Reading from file :"); //
144
APPENDLOG(fileName); //
146
return ObjRead(interp, data, format, imageHandle, destX, destY, width, height, srcX, srcY);
150
int ObjRead (Tcl_Interp *interp, Tcl_Obj *data, Tcl_Obj *format, Tk_PhotoHandle imageHandle,
151
int destX, int destY, int width, int height, int srcX, int srcY)
154
BYTE * buffer = NULL;
157
BYTE * FileData = NULL;
162
LOG("Reading data :"); //
164
FileData = Tcl_GetByteArrayFromObj(data, &length);
167
if (! image.Decode(FileData, length, CXIMAGE_FORMAT_GIF) &&
168
! image.Decode(FileData, length, CXIMAGE_FORMAT_PNG) &&
169
! image.Decode(FileData, length, CXIMAGE_FORMAT_JPG) &&
170
! image.Decode(FileData, length, CXIMAGE_FORMAT_TGA) &&
171
! image.Decode(FileData, length, CXIMAGE_FORMAT_BMP))
175
int numframes = image.GetNumFrames();
181
if(!image.Crop(srcX, srcY, srcX + width, srcY + height)) {
182
Tcl_AppendResult(interp, image.GetLastError(), NULL);
186
LOG("Flipping image"); //
189
Tcl_AppendResult(interp, image.GetLastError(), NULL);
193
LOG("Encoding to RGBA"); //
195
if(!image.Encode2RGBA(buffer, size)) {
196
Tcl_AppendResult(interp, image.GetLastError(), NULL);
200
LOG("Setting PhotoImageBlock"); //
202
Tk_PhotoImageBlock block = {
206
width*4, // pitch : number of bytes separating 2 adjacent pixels vertically
207
4, // pixel size : size in bytes of one pixel .. 4 = RGBA
214
if ( image.AlphaIsValid() || image.IsTransparent() ) {
215
LOG("Alpha is valid, setting offset[3]"); //
219
LOG("Putting Block into image"); //
220
#if TK_MINOR_VERSION == 3
221
Tk_PhotoBlank(imageHandle);
222
Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height);
224
#if TK_MINOR_VERSION == 4
225
Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height, TK_PHOTO_COMPOSITE_SET);
227
#if TK_MINOR_VERSION == 5
228
Tk_PhotoPutBlock((Tcl_Interp *) NULL, imageHandle, &block, destX, destY, width, height, TK_PHOTO_COMPOSITE_SET);
235
APPENDLOG(imageHandle);
236
GifInfo* item=TkCxImage_lstGetItem(imageHandle);
238
LOG("Got item in Animated list");
239
Tcl_DeleteTimerHandler(item->timerToken);
240
item->image->DestroyGifFrames();
242
for(GifBuffersIterator it=item->buffers.begin(); it!=item->buffers.end(); it++){
246
LOG("Deleting AnimatedGifInfo");
247
APPENDLOG(item->Handle);
248
TkCxImage_lstDeleteItem(item->Handle);
251
// If it's an animated gif, take care of it right here
252
if(g_EnableAnimated && numframes > 1) {
254
GifInfo * AnimatedGifInfo = new GifInfo;
255
CxImage *image = NULL;
257
AnimatedGifInfo->CurrentFrame = 0;
258
AnimatedGifInfo->CopiedFrame = -1;
259
AnimatedGifInfo->NumFrames = numframes;
260
AnimatedGifInfo->Handle = imageHandle;
261
AnimatedGifInfo->ImageMaster = (Tk_ImageMaster) *((void **)imageHandle);
262
AnimatedGifInfo->interp = interp;
263
AnimatedGifInfo->image = new CxImage;
264
AnimatedGifInfo->image->RetreiveAllFrame();
265
AnimatedGifInfo->image->SetFrame(numframes - 1);
266
AnimatedGifInfo->image->Decode(FileData, length, CXIMAGE_FORMAT_GIF);
268
for(int i = 0; i < numframes; i++){
269
if(AnimatedGifInfo->image->GetFrameNo(i) != AnimatedGifInfo->image) {
270
AnimatedGifInfo->image->GetFrameNo(i)->Flip();
273
LOG("Adding AnimatedGifInfo");
274
APPENDLOG(imageHandle);
275
TkCxImage_lstAddItem(AnimatedGifInfo);
279
for(int i = 0; i < numframes; i++){
280
currentFrame = new CxImage();
281
currentFrame->SetFrame(i);
282
if(currentFrame->Decode(FileData, length, CXIMAGE_FORMAT_GIF) && currentFrame->Flip()) {
283
AnimatedGifInfo->Frames[i] = currentFrame;
286
for(int i = 0; i < numframes; i++){
287
delete AnimatedGifInfo->Frames[i];
288
AnimatedGifInfo->Frames[i] = NULL;
290
delete AnimatedGifInfo->Frames;
291
AnimatedGifInfo->Frames = NULL;
292
delete AnimatedGifInfo;
293
AnimatedGifInfo = NULL;
298
AnimatedGifInfo->timerToken=Tcl_CreateTimerHandler(AnimatedGifInfo->image->GetFrameNo(0)->GetFrameDelay(), AnimateGif, (ClientData) AnimatedGifInfo);
301
#endif // ANIMATE_GIFS
303
LOG("Freeing memory used by buffer"); //
304
image.FreeMemory(buffer);
309
int ChanWrite (Tcl_Interp *interp, CONST char *fileName, Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr) {
312
int Type = CXIMAGE_FORMAT_UNKNOWN;
313
char * cxFormat = NULL;
315
BYTE * pixelPtr = NULL;
318
cxFormat = Tcl_GetStringFromObj(format, NULL);
319
Type = GetFileTypeFromFormat(cxFormat);
322
if (Type == CXIMAGE_FORMAT_UNKNOWN) {
323
Type = GetFileTypeFromFileName((char *) fileName);
326
if (Type == CXIMAGE_FORMAT_UNKNOWN) {
327
Type = CXIMAGE_FORMAT_GIF;
330
pixelPtr = (BYTE *) malloc(blockPtr->width * blockPtr->height * blockPtr->pixelSize);
332
if (RGB2BGR(blockPtr, pixelPtr)) {
336
if(!image.CreateFromArray(pixelPtr, blockPtr->width, blockPtr->height,
337
8 * blockPtr->pixelSize, blockPtr->pitch, true))
340
Tcl_AppendResult(interp, image.GetLastError(), NULL);
348
if (Type == CXIMAGE_FORMAT_GIF)
349
image.DecreaseBpp(8, true);
351
if (!image.Save(fileName, Type)) {
352
Tcl_AppendResult(interp, image.GetLastError(), NULL);
359
int StringWrite (Tcl_Interp *interp, Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr) {
361
BYTE * buffer = NULL;
363
int Type = CXIMAGE_FORMAT_UNKNOWN;
364
char * cxFormat = NULL;
365
BYTE * pixelPtr = NULL;
370
cxFormat = Tcl_GetStringFromObj(format, NULL);
371
Type = GetFileTypeFromFormat(cxFormat);
374
if (Type == CXIMAGE_FORMAT_UNKNOWN) {
375
Type = CXIMAGE_FORMAT_GIF;
378
pixelPtr = (BYTE *) malloc(blockPtr->width * blockPtr->height * blockPtr->pixelSize);
380
if (RGB2BGR(blockPtr, pixelPtr)) {
384
if(!image.CreateFromArray(pixelPtr, blockPtr->width, blockPtr->height,
385
8 * blockPtr->pixelSize, blockPtr->pitch, true))
388
Tcl_AppendResult(interp, image.GetLastError(), NULL);
396
if (Type == CXIMAGE_FORMAT_GIF)
397
image.DecreaseBpp(8, true);
400
if (!image.Encode(buffer, size, Type) ) {
401
Tcl_AppendResult(interp, image.GetLastError(), NULL);
405
Tcl_ResetResult(interp);
406
Tcl_AppendResult(interp, buffer);
408
image.FreeMemory(buffer);
415
void AnimateGif(ClientData data) {
416
GifInfo *Info = (GifInfo *)data;
417
if (Info) { //Info is valid
418
Tk_ImageMaster master = (Tk_ImageMaster) *((void **) Info->Handle);
419
if(master == Info->ImageMaster) {
420
//Image is always the same
421
if(g_EnableAnimated) {
422
Info->CurrentFrame++;
423
if(Info->CurrentFrame == Info->NumFrames)
424
Info->CurrentFrame = 0;
425
CxImage *image = Info->image->GetFrameNo(Info->CurrentFrame);
426
Tk_ImageChanged(Info->ImageMaster, 0, 0, image->GetWidth(), image->GetHeight(), image->GetWidth(), image->GetHeight());
428
Info->timerToken=Tcl_CreateTimerHandler(image->GetFrameDelay()?10*image->GetFrameDelay():40, AnimateGif, data);
430
int currentFrame = Info->CurrentFrame;
431
CxImage *image = Info->image->GetFrameNo(currentFrame);
432
Info->timerToken=Tcl_CreateTimerHandler(image->GetFrameDelay()?10*image->GetFrameDelay():40, AnimateGif, data);
435
LOG("Image destroyed, deleting... Image Master was : ");
438
APPENDLOG( Info->ImageMaster);
440
Info->image->DestroyGifFrames();
442
LOG("Deleting AnimatedGifInfo");
443
APPENDLOG(Info->Handle);
444
TkCxImage_lstDeleteItem(Info->Handle);
445
for(GifBuffersIterator it=Info->buffers.begin(); it!=Info->buffers.end(); it++){
456
void PhotoDisplayProcHook(
457
ClientData instanceData,
471
* The whole next block is used to prevent a bug with XGetImage
472
* that happens with Tcl/Tk before 8.4.9 that caused a BadMatch.
476
unsigned int drawableWidth_geo;
477
unsigned int drawableHeight_geo;
479
unsigned int depth_geo;
481
// Make sure there's something to draw
482
if (width < 1 || height < 1) {
486
// Get the drawable's width and height and x and y
487
switch (XGetGeometry(display, drawable, &root_geo, &x_geo, &y_geo,
488
&drawableWidth_geo, &drawableHeight_geo, &bd_geo, &depth_geo)) {
492
Tcl_Panic("ClipSizeForDrawable: invalid drawable passed");
496
// Make sure we're not requesting a width or heigth more than allowed
497
if (width > drawableWidth_geo) {
498
width = drawableWidth_geo;
501
if (height > drawableHeight_geo) {
502
height = drawableHeight_geo;
505
// Make sure the coordinates are valid
521
Tk_PhotoHandle handle = (Tk_PhotoHandle) *((void **) instanceData);
522
GifInfo* item=TkCxImage_lstGetItem(handle);
524
if (item->CurrentFrame != item->CopiedFrame) { //Frame isn't the good one in the photo buffer
525
CxImage *image = item->image->GetFrameNo(item->CurrentFrame);
526
item->CopiedFrame = item->CurrentFrame; //We set the copied frame before to avoid infinite loops
527
AnimatedGifFrameToTk(NULL, item, image, true);
528
//fprintf(stderr, "Copied frame n°%u\n",item->CopiedFrame);
535
PhotoDisplayOriginal(instanceData,display,drawable,imageX,imageY,width,height,drawableX,drawableY);
538
#endif // ANIMATE_GIFS