diff options
author | simonbachmann <simonbachmann@bluewin.ch> | 2017-04-30 09:04:00 (GMT) |
---|---|---|
committer | simonbachmann <simonbachmann@bluewin.ch> | 2017-04-30 09:04:00 (GMT) |
commit | 40501b1ef03d1d2748a78033cb1ac5209b9ea706 (patch) | |
tree | c05a03641ecaf1ba37ec99d7b63f4a9ed652f064 /generic | |
parent | 1ecfca87f2dc7f8676bc0aca4d724bcb00f68b31 (diff) | |
download | tk-40501b1ef03d1d2748a78033cb1ac5209b9ea706.zip tk-40501b1ef03d1d2748a78033cb1ac5209b9ea706.tar.gz tk-40501b1ef03d1d2748a78033cb1ac5209b9ea706.tar.bz2 |
Fix [e4336bef5d] (Unexpected result when copying a photo image to itself): if source and destination image are the same, make a local copy of image data before the actual copy/zoom/subsample.
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkImgPhoto.c | 166 |
1 files changed, 120 insertions, 46 deletions
diff --git a/generic/tkImgPhoto.c b/generic/tkImgPhoto.c index 1bd0142..8b7350d 100644 --- a/generic/tkImgPhoto.c +++ b/generic/tkImgPhoto.c @@ -2698,7 +2698,7 @@ Tk_PhotoPutBlock( * messages, or NULL. */ Tk_PhotoHandle handle, /* Opaque handle for the photo image to be * updated. */ - register Tk_PhotoImageBlock *blockPtr, + Tk_PhotoImageBlock *blockPtr, /* Pointer to a structure describing the pixel * data to be copied into the image. */ int x, int y, /* Coordinates of the top-left pixel to be @@ -2709,6 +2709,8 @@ Tk_PhotoPutBlock( * transparent pixels. */ { register PhotoMaster *masterPtr = (PhotoMaster *) handle; + Tk_PhotoImageBlock sourceBlock; + unsigned char *memToFree; int xEnd, yEnd, greenOffset, blueOffset, alphaOffset; int wLeft, hLeft, wCopy, hCopy, pitch; unsigned char *srcPtr, *srcLinePtr, *destPtr, *destLinePtr; @@ -2736,11 +2738,43 @@ Tk_PhotoPutBlock( return TCL_OK; } + /* + * Fix for bug e4336bef5d: + * + * Make a local copy of *blockPtr, as we might have to change some + * of its fields and don't want to interfere with the caller's data. + * + * If source and destination are the same image, create a copy of the + * source data in our local sourceBlock. + * + * To find out, just comparing the pointers is not enough - they might have + * different values and still point to the same block of memory. (e.g. + * if the -from option was passed to [imageName copy]) + */ + sourceBlock = *blockPtr; + memToFree = NULL; + if (sourceBlock.pixelPtr >= masterPtr->pix32 + && sourceBlock.pixelPtr <= masterPtr->pix32 + masterPtr->width + * masterPtr->height * 4) { + sourceBlock.pixelPtr = attemptckalloc(sourceBlock.height + * sourceBlock.pitch); + if (sourceBlock.pixelPtr == NULL) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); + } + return TCL_ERROR; + } + memToFree = sourceBlock.pixelPtr; + memcpy(sourceBlock.pixelPtr, blockPtr->pixelPtr, sourceBlock.height + * sourceBlock.pitch); + } + + xEnd = x + width; yEnd = y + height; if ((xEnd > masterPtr->width) || (yEnd > masterPtr->height)) { - int sameSrc = (blockPtr->pixelPtr == masterPtr->pix32); - if (ImgPhotoSetSize(masterPtr, MAX(xEnd, masterPtr->width), MAX(yEnd, masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { @@ -2748,11 +2782,7 @@ Tk_PhotoPutBlock( TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } - return TCL_ERROR; - } - if (sameSrc) { - blockPtr->pixelPtr = masterPtr->pix32; - blockPtr->pitch = masterPtr->width * 4; + goto errorExit; } } @@ -2771,14 +2801,14 @@ Tk_PhotoPutBlock( * components, mark it as a color image. */ - greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; - blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; - alphaOffset = blockPtr->offset[3]; - if ((alphaOffset >= blockPtr->pixelSize) || (alphaOffset < 0)) { + greenOffset = sourceBlock.offset[1] - sourceBlock.offset[0]; + blueOffset = sourceBlock.offset[2] - sourceBlock.offset[0]; + alphaOffset = sourceBlock.offset[3]; + if ((alphaOffset >= sourceBlock.pixelSize) || (alphaOffset < 0)) { alphaOffset = 0; sourceIsSimplePhoto = 1; } else { - alphaOffset -= blockPtr->offset[0]; + alphaOffset -= sourceBlock.offset[0]; } if ((greenOffset != 0) || (blueOffset != 0)) { masterPtr->flags |= COLOR_IMAGE; @@ -2798,13 +2828,13 @@ Tk_PhotoPutBlock( * pixelSize == 3 and alphaOffset == 0. Maybe other cases too. */ - if ((blockPtr->pixelSize == 4) + if ((sourceBlock.pixelSize == 4) && (greenOffset == 1) && (blueOffset == 2) && (alphaOffset == 3) - && (width <= blockPtr->width) && (height <= blockPtr->height) + && (width <= sourceBlock.width) && (height <= sourceBlock.height) && ((height == 1) || ((x == 0) && (width == masterPtr->width) - && (blockPtr->pitch == pitch))) + && (sourceBlock.pitch == pitch))) && (compRule == TK_PHOTO_COMPOSITE_SET)) { - memmove(destLinePtr, blockPtr->pixelPtr + blockPtr->offset[0], + memmove(destLinePtr, sourceBlock.pixelPtr + sourceBlock.offset[0], ((size_t)height * width * 4)); /* @@ -2820,11 +2850,11 @@ Tk_PhotoPutBlock( */ for (hLeft = height; hLeft > 0;) { - int pixelSize = blockPtr->pixelSize; + int pixelSize = sourceBlock.pixelSize; int compRuleSet = (compRule == TK_PHOTO_COMPOSITE_SET); - srcLinePtr = blockPtr->pixelPtr + blockPtr->offset[0]; - hCopy = MIN(hLeft, blockPtr->height); + srcLinePtr = sourceBlock.pixelPtr + sourceBlock.offset[0]; + hCopy = MIN(hLeft, sourceBlock.height); hLeft -= hCopy; for (; hCopy > 0; --hCopy) { /* @@ -2835,10 +2865,10 @@ Tk_PhotoPutBlock( if ((pixelSize == 4) && (greenOffset == 1) && (blueOffset == 2) && (alphaOffset == 3) - && (width <= blockPtr->width) + && (width <= sourceBlock.width) && compRuleSet) { memcpy(destLinePtr, srcLinePtr, ((size_t)width * 4)); - srcLinePtr += blockPtr->pitch; + srcLinePtr += sourceBlock.pitch; destLinePtr += pitch; continue; } @@ -2849,7 +2879,7 @@ Tk_PhotoPutBlock( destPtr = destLinePtr; for (wLeft = width; wLeft > 0;) { - wCopy = MIN(wLeft, blockPtr->width); + wCopy = MIN(wLeft, sourceBlock.width); wLeft -= wCopy; srcPtr = srcLinePtr; @@ -2939,7 +2969,7 @@ Tk_PhotoPutBlock( destPtr += 4; } } - srcLinePtr += blockPtr->pitch; + srcLinePtr += sourceBlock.pitch; destLinePtr += pitch; } } @@ -3055,7 +3085,15 @@ Tk_PhotoPutBlock( Tk_ImageChanged(masterPtr->tkMaster, x, y, width, height, masterPtr->width, masterPtr->height); + + ckfree(memToFree); + return TCL_OK; + + errorExit: + ckfree(memToFree); + + return TCL_ERROR; } /* @@ -3082,7 +3120,7 @@ Tk_PhotoPutZoomedBlock( * messages, or NULL. */ Tk_PhotoHandle handle, /* Opaque handle for the photo image to be * updated. */ - register Tk_PhotoImageBlock *blockPtr, + Tk_PhotoImageBlock *blockPtr, /* Pointer to a structure describing the pixel * data to be copied into the image. */ int x, int y, /* Coordinates of the top-left pixel to be @@ -3097,6 +3135,8 @@ Tk_PhotoPutZoomedBlock( * transparent pixels. */ { register PhotoMaster *masterPtr = (PhotoMaster *) handle; + register Tk_PhotoImageBlock sourceBlock; + unsigned char *memToFree; int xEnd, yEnd, greenOffset, blueOffset, alphaOffset; int wLeft, hLeft, wCopy, hCopy, blockWid, blockHt; unsigned char *srcPtr, *srcLinePtr, *srcOrigPtr, *destPtr, *destLinePtr; @@ -3132,12 +3172,42 @@ Tk_PhotoPutZoomedBlock( if (width <= 0 || height <= 0) { return TCL_OK; } + + /* + * Fix for Bug e4336bef5d: + * Make a local copy of *blockPtr, as we might have to change some + * of its fields and don't want to interfere with the caller's data. + * + * If source and destination are the same image, create a copy of the + * source data in our local sourceBlock. + * + * To find out, just comparing the pointers is not enough - they might have + * different values and still point to the same block of memory. (e.g. + * if the -from option was passed to [imageName copy]) + */ + sourceBlock = *blockPtr; + memToFree = NULL; + if (sourceBlock.pixelPtr >= masterPtr->pix32 + && sourceBlock.pixelPtr <= masterPtr->pix32 + masterPtr->width + * masterPtr->height * 4) { + sourceBlock.pixelPtr = attemptckalloc(sourceBlock.height + * sourceBlock.pitch); + if (sourceBlock.pixelPtr == NULL) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); + } + return TCL_ERROR; + } + memToFree = sourceBlock.pixelPtr; + memcpy(sourceBlock.pixelPtr, blockPtr->pixelPtr, sourceBlock.height + * sourceBlock.pitch); + } xEnd = x + width; yEnd = y + height; if ((xEnd > masterPtr->width) || (yEnd > masterPtr->height)) { - int sameSrc = (blockPtr->pixelPtr == masterPtr->pix32); - if (ImgPhotoSetSize(masterPtr, MAX(xEnd, masterPtr->width), MAX(yEnd, masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { @@ -3145,11 +3215,7 @@ Tk_PhotoPutZoomedBlock( TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } - return TCL_ERROR; - } - if (sameSrc) { - blockPtr->pixelPtr = masterPtr->pix32; - blockPtr->pitch = masterPtr->width * 4; + goto errorExit; } } @@ -3168,14 +3234,14 @@ Tk_PhotoPutZoomedBlock( * components, mark it as a color image. */ - greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; - blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; - alphaOffset = blockPtr->offset[3]; - if ((alphaOffset >= blockPtr->pixelSize) || (alphaOffset < 0)) { + greenOffset = sourceBlock.offset[1] - sourceBlock.offset[0]; + blueOffset = sourceBlock.offset[2] - sourceBlock.offset[0]; + alphaOffset = sourceBlock.offset[3]; + if ((alphaOffset >= sourceBlock.pixelSize) || (alphaOffset < 0)) { alphaOffset = 0; sourceIsSimplePhoto = 1; } else { - alphaOffset -= blockPtr->offset[0]; + alphaOffset -= sourceBlock.offset[0]; } if ((greenOffset != 0) || (blueOffset != 0)) { masterPtr->flags |= COLOR_IMAGE; @@ -3186,21 +3252,21 @@ Tk_PhotoPutZoomedBlock( * subsampling and zooming. */ - blockXSkip = subsampleX * blockPtr->pixelSize; - blockYSkip = subsampleY * blockPtr->pitch; + blockXSkip = subsampleX * sourceBlock.pixelSize; + blockYSkip = subsampleY * sourceBlock.pitch; if (subsampleX > 0) { - blockWid = ((blockPtr->width + subsampleX - 1) / subsampleX) * zoomX; + blockWid = ((sourceBlock.width + subsampleX - 1) / subsampleX) * zoomX; } else if (subsampleX == 0) { blockWid = width; } else { - blockWid = ((blockPtr->width - subsampleX - 1) / -subsampleX) * zoomX; + blockWid = ((sourceBlock.width - subsampleX - 1) / -subsampleX) * zoomX; } if (subsampleY > 0) { - blockHt = ((blockPtr->height + subsampleY - 1) / subsampleY) * zoomY; + blockHt = ((sourceBlock.height + subsampleY - 1) / subsampleY) * zoomY; } else if (subsampleY == 0) { blockHt = height; } else { - blockHt = ((blockPtr->height - subsampleY - 1) / -subsampleY) * zoomY; + blockHt = ((sourceBlock.height - subsampleY - 1) / -subsampleY) * zoomY; } /* @@ -3208,12 +3274,12 @@ Tk_PhotoPutZoomedBlock( */ destLinePtr = masterPtr->pix32 + (y * masterPtr->width + x) * 4; - srcOrigPtr = blockPtr->pixelPtr + blockPtr->offset[0]; + srcOrigPtr = sourceBlock.pixelPtr + sourceBlock.offset[0]; if (subsampleX < 0) { - srcOrigPtr += (blockPtr->width - 1) * blockPtr->pixelSize; + srcOrigPtr += (sourceBlock.width - 1) * sourceBlock.pixelSize; } if (subsampleY < 0) { - srcOrigPtr += (blockPtr->height - 1) * blockPtr->pitch; + srcOrigPtr += (sourceBlock.height - 1) * sourceBlock.pitch; } pitch = masterPtr->width * 4; @@ -3363,7 +3429,15 @@ Tk_PhotoPutZoomedBlock( Tk_ImageChanged(masterPtr->tkMaster, x, y, width, height, masterPtr->width, masterPtr->height); + + ckfree(memToFree); + return TCL_OK; + + errorExit: + ckfree(memToFree); + + return TCL_ERROR; } /* |