Bug 4764 - DCR: Support for blit operations within ImageData (1GH43Y2) (paint on Image and alpha channel)
Summary: DCR: Support for blit operations within ImageData (1GH43Y2) (paint on Image a...
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 2.0   Edit
Hardware: All All
: P4 normal (vote)
Target Milestone: ---   Edit
Assignee: Silenio Quarti CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 165253
  Show dependency tree
 
Reported: 2001-10-11 14:22 EDT by Mike Wilson CLA
Modified: 2019-09-06 16:12 EDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Wilson CLA 2001-10-11 14:22:36 EDT
Since some of our ImageData users are apparently performing block
	image transfers in user code, we should offer them an easier solution.
	Moreover, this would provide an elegant means of converting ImageData's
	between data formats for easier manipulation.  For instance, transforming an
	image loaded as a 256 color GIF into a suitable candidate for RGB-based
	photo editing.  Doing this now is very difficult.

	A sample (untested) implementation follows.  It makes use of recently
	enhanced internal blitter functionality in ImageData and exposes only what is
	now available.

NOTES:

/**
 * Copies a portion of the receiver into the supplied
 * destination ImageData.
 * <p>
 * If the absolute value of the destination width or
 * height differs from that of the source, the image is
 * stretched or shrunked accordingly to fit.
 * </p><p>
 * The top-left coordinate of the source region is at
 * [min(srcX, srcX + srcWidth), min(srcY, srcY + srcHeight)]<br>
 * The bottom-right coordinate of the source region is at
 * [max(srcX - 1, srcX + srcWidth - 1), max(srcY - 1, srcY + srcWidth - 1)]<br>
 * If either the width or height is negative, the pixels
 * of the source region will be inverted along the
 * associated axis before copying.<br>
 * The same rules apply for the destination region.
 * </p><p>
 * If both the source width and destination width are
 * negative, no inversion will be performed along the
 * the vertical axis; similarly for the source height
 * and destination height.
 * </p><p>
 * The blitter operation is a combination of the following
 * flags:<ul>
 *   <li>BLIT_SRC copy the source pixels exactly
 *          (must be specified, there may be other
 *          mechanisms available in the future)
 *   <li>BLIT_ALPHA perform alpha blending or transparency
 *          masking
 *   <li>BLIT_DITHER perform error-diffusion dithering on
 *          low color destinations (hint)
 * </ul>
 * Typically one would specify BLIT_SRC | BLIT_ALPHA.
 * </p><p>
 * Blitting overlapping regions on the same ImageData
 * is supported (but may be slower).
 * </p>
 *
 * @param dest the destination ImageData into which a
 *        portion of the receiver will be copied
 * @param blitOp the blitter operation to be performed
 * @param srcX the X coordinate defining the source region
 * @param srcY the Y coordinate defining the source region
 * @param srcWidth the width of the source region, may be negative
 * @param srcHeight the height of the source region, may be negative
 * @param destX the X coordinate defining the destination region
 * @param destY the Y coordinate defining the destination region
 * @param destWidth the width of the destination region, may be negative
 * @param destHeight the height of the destination region, may be negative
 */
public void blitTo(ImageData dest, int blitOp,
	int srcX, int srcY, int srcWidth, int srcHeight,
	int destX, int destY, int destWidth, int destHeight) {
	boolean flipX = srcWidth < 0;
	if (flipX) { srcX += srcWidth; srcWidth = - srcWidth; }
	if (destWidth < 0) { flipX = ! flipX; destX += destWidth; destWidth = - destWidth; }

	boolean flipY = srcHeight < 0;
	if (flipY) { srcY += srcHeight; srcHeight = - srcHeight; }
	if (destHeight < 0) { flipY = ! flipY; destY += destHeight; destHeight = - destHeight; }

	if (dest == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
	if ((srcX < 0) || (srcX + srcWidth > this.width) ||
		(srcY < 0) || (srcY + srcHeight > this.height) ||
		(destX < 0) || (destX + destWidth > dest.width) ||
		(destY < 0) || (destY + destHeight > dest.height)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);

	// Handle alpha and transparency
	int alphaOp = ALPHA_OPAQUE;
	byte[] alphaData = null;
	int alphaStride = 0;
	if ((blitOp & BLIT_ALPHA) != 0) {
		if (maskData != null) {
			alphaOp = ALPHA_MASK_PACKED;
			alphaData = this.maskData;
			final int bpl = (this.width + 7) / 8;
			alphaStride = (bpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
		} else if (transparentPixel != -1) {
			if (this.palette.isDirect) {
				alphaOp = ALPHA_MASK_RGB;
				alphaData = new byte[] {
					getChannelField(this.transparentPixel, this.palette.redMask),
					getChannelField(this.transparentPixel, this.palette.greenMask),
					getChannelField(this.transparentPixel, this.palette.blueMask)
				};
			} else {
				alphaOp = ALPHA_MASK_INDEX;
				alphaData = new byte[] { (byte)this.transparentPixel };
			}
			alphaStride = 0;
		} else if (alpha != -1) {
			alphaOp = alpha;
			alphaData = null;
			alphaStride = 0;
		} else if (alphaData != null) {
			alphaOp = ALPHA_CHANNEL_SEPARATE;
			alphaData = this.alphaData;
			alphaStride = this.width;
		}
		if (alphaOp == ALPHA_OPAQUE) blitOp &= ~BLIT_ALPHA;
		else if (alphaOp == ALPHA_TRANSPARENT) return;
	}

	// Handle overlapping regions (a bit bogus)
	final byte[] srcData;
	if ((srcX + srcWidth < destX) || (destX + destWidth < srcX) ||
		(srcY + srcHeight < destY) || (destY + destHeight < srcY)) {
		// regions are disjoint
		srcData = this.data;
	} else {
		// regions overlap
		srcData = new byte[srcHeight * this.bytesPerLine];
		System.arraycopy(this.data, srcY * this.bytesPerLine, srcData, 0, srcData.length);
		srcY = 0;
	}
	
	// Do the blit
	if (this.palette.isDirect) {
		if (dest.palette.isDirect) {
			blit(blitOp,
				srcData, this.depth, this.bytesPerLine, this.getByteOrder(),
				srcX, srcY, srcWidth, srcHeight,
				this.palette.redMask, this.palette.greenMask, this.palette.blueMask,
				alphaOp, alphaData, alphaStride,
				dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(),
				destX, destY, destWidth, destHeight,
				dest.palette.redMask, dest.palette.greenMask, dest.palette.blueMask,
				flipX, flipY);
		} else {
			final int destPaletteSize = 1 << dest.depth;
			final byte[] destReds = new byte[destPaletteSize];
			final byte[] destGreens = new byte[destPaletteSize];
			final byte[] destBlues = new byte[destPaletteSize];
			for (int i = Math.min(destPaletteSize, dest.palette.colors.length) - 1; i >= 0; --i) {
				final RGB rgb = dest.palette.colors[i];
				destReds[i] = (byte)rgb.red; destGreens[i] = (byte)rgb.green; destBlues[i] = (byte)rgb.blue;
			}
			blit(blitOp,
				srcData, this.depth, this.bytesPerLine, this.getByteOrder(),
				srcX, srcY, srcWidth, srcHeight,
				this.palette.redMask, this.palette.greenMask, this.palette.blueMask,
				alphaOp, alphaData, alphaStride,
				dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(),
				destX, destY, destWidth, destHeight,
				destReds, destGreens, destBlues,
				flipX, flipY);
		}
	} else {
		final int srcPaletteSize = 1 << this.depth;
		final byte[] srcReds = new byte[srcPaletteSize];
		final byte[] srcGreens = new byte[srcPaletteSize];
		final byte[] srcBlues = new byte[srcPaletteSize];
		for (int i = Math.min(srcPaletteSize, this.palette.colors.length) - 1; i >= 0; --i) {
			final RGB rgb = this.palette.colors[i];
			srcReds[i] = (byte)rgb.red; srcGreens[i] = (byte)rgb.green; srcBlues[i] = (byte)rgb.blue;
		}
		if (dest.palette.isDirect) {
			blit(blitOp,
				srcData, this.depth, this.bytesPerLine, this.getByteOrder(),
				srcX, srcY, srcWidth, srcHeight,
				srcReds, srcGreens, srcBlues,
				alphaOp, alphaData, alphaStride,
				dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(),
				destX, destY, destWidth, destHeight,
				dest.palette.redMask, dest.palette.greenMask, dest.palette.blueMask,
				flipX, flipY);
		} else {
			final byte[] destReds;
			final byte[] destGreens;
			final byte[] destBlues;
			if ((this.depth <= dest.depth) && (this.palette.colors == dest.palette.colors)) {
				destReds = srcReds;
				destGreens = srcGreens;
				destBlues = srcBlues;
			} else {		
				final int destPaletteSize = 1 << this.depth;
				destReds = new byte[destPaletteSize];
				destGreens = new byte[destPaletteSize];
				destBlues = new byte[destPaletteSize];
				for (int i = Math.min(destPaletteSize, this.palette.colors.length) - 1; i >= 0; --i) {
					final RGB rgb = this.palette.colors[i];
					destReds[i] = (byte)rgb.red; destGreens[i] = (byte)rgb.green; destBlues[i] = (byte)rgb.blue;
				}
			}
			blit(blitOp,
				srcData, this.depth, this.bytesPerLine, this.getByteOrder(),
				srcX, srcY, srcWidth, srcHeight,
				srcReds, srcGreens, srcBlues,
				alphaOp, alphaData, alphaStride,
				dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(),
				destX, destY, destWidth, destHeight,
				destReds, destGreens, destBlues,
				flipX, flipY);
		}
	}
}
Comment 1 DJ Houghton CLA 2001-10-29 16:36:43 EST
PRODUCT VERSION:
	Build 125

Comment 2 Tod Creasey CLA 2006-06-14 11:39:38 EDT
We require this API as well so that we can improve the JFace CompositeImageDescriptor. What we want to do is take two or  imageDatas and lay them on top of each other using the transparency, palette, etc. of the base ImageData.
Comment 3 Mike Wilson CLA 2006-06-14 14:00:08 EDT
Exactly what is being proposed here? How general would this support be? How would transparency be handled (i.e. do you understand what the result would be for each kind of transparency + how would this compare to the Image to Image copy case)? Would we have native support on platforms that provide it? Should the given code be converted to a C native for performance reasons?
Comment 4 Randy Hudson CLA 2006-06-15 08:56:33 EDT
I think the main limitation is that when painting an Image on top of another Image (using new GC(Image)), the transparency masks are not combined in a meaningful way.  There is the same limitation when drawing lines, circles, etc., onto an Image. So, I see two routes. Start writing an emulated version of a GC that operates on ImageData instead of an image. Or, support some extra modes on GC that tell it to paint on the transparency mask in addition to the image's source.
Comment 5 Steve Northover CLA 2006-06-15 10:06:53 EDT
Yup, these are good ideas that we are already considering.
Comment 6 Tod Creasey CLA 2007-03-20 20:08:50 EDT
We are going to have to do something for JFace at least in the 3.3 timeframe as community pressure is rising steadily. 

Silenio and Steve can we talk about this early in M7?
Comment 7 Randy Hudson CLA 2007-03-20 23:19:30 EDT
Using just the Eclipse SDK, when I enable all image decorators, Eclipse allocates almost 100 new Images, which are globally held until the workbench shuts down.  Add a few more editor types, team providers, etc., and the resource situation gets even worse. The current implementation creates x*a*b*c*d Images to display a+b+c+d decorators on top of x base Images.

Instead of improving CompositeImageDescriptor/ImageData, would an alternative be to switch to using owner draw? Could we not just paint the decorations on top of the TreeItem?
Comment 8 Randy Hudson CLA 2007-11-09 15:00:45 EST
What's the status here? Is this mode of GC painting on an Image supported on any subset of the windowing toolkits?
Comment 9 Eclipse Webmaster CLA 2019-09-06 16:12:24 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.