# HG changeset patch
# User Frank Benoit
+ * The DWT drawing coordinate system is the two-dimensional space with the origin
+ * (0,0) at the top left corner of the drawing area and with (x,y) values increasing
+ * to the right and downward respectively.
+ *
+ * Application code must explicitly invoke the
+ * Note: Only one of LEFT_TO_RIGHT and RIGHT_TO_LEFT may be specified.
+ *
+ * IMPORTANT: This field is not part of the DWT
+ * public API. It is marked public only so that it can be shared
+ * within the packages provided by DWT. It is not available on all
+ * platforms and should never be accessed from application code.
+ *
+ * You must dispose the graphics context when it is no longer required.
+ *
+ * You must dispose the graphics context when it is no longer required.
+ *
+ * The resulting arc begins at
+ * The center of the arc is the center of the rectangle whose origin
+ * is (
+ * The resulting arc covers an area
+ * The result is a circle or ellipse that fits within the
+ * rectangle specified by the
+ * The oval covers an area that is
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ *
+ * Note that the receiver's line attributes do not affect this
+ * operation.
+ *
+ * The parameter GC
is where all of the drawing capabilities that are
+ * supported by DWT are located. Instances are used to draw on either an
+ * Image
, a Control
, or directly on a Display
.
+ *
+ *
+ *
+ * GC.dispose()
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required. This is particularly
+ * important on Windows95 and Windows98 where the operating system has a limited
+ * number of device contexts available.
+ *
+ *
+ * @exception DWTError
+ *
+ */
+public GC(Drawable drawable) {
+ this(drawable, DWT.NONE);
+}
+
+/**
+ * Constructs a new instance of this class which has been
+ * configured to draw on the specified drawable. Sets the
+ * foreground color, background color and font in the GC
+ * to match those in the drawable.
+ *
+ *
+ * @exception DWTError
+ *
+ *
+ * @since 2.1.2
+ */
+public GC(Drawable drawable, int style) {
+ if (drawable is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ GCData data = new GCData ();
+ data.style = checkStyle(style);
+ int hDC = drawable.internal_new_GC(data);
+ Device device = data.device;
+ if (device is null) device = Device.getDevice();
+ if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ this.device = data.device = device;
+ init (drawable, data, hDC);
+ if (device.tracking) device.new_Object(this);
+}
+
+static int checkStyle(int style) {
+ if ((style & DWT.LEFT_TO_RIGHT) !is 0) style &= ~DWT.RIGHT_TO_LEFT;
+ return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
+}
+
+void checkGC(int mask) {
+ int state = data.state;
+ if ((state & mask) is mask) return;
+ state = (state ^ mask) & mask;
+ data.state |= mask;
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ int pen = data.gdipPen;
+ float width = data.lineWidth;
+ if ((state & FOREGROUND) !is 0 || (pen is 0 && (state & (LINE_WIDTH | LINE_STYLE | LINE_MITERLIMIT | LINE_JOIN | LINE_CAP)) !is 0)) {
+ if (data.gdipFgBrush !is 0) Gdip.SolidBrush_delete(data.gdipFgBrush);
+ data.gdipFgBrush = 0;
+ int brush;
+ Pattern pattern = data.foregroundPattern;
+ if (pattern !is null) {
+ brush = pattern.handle;
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ switch (Gdip.Brush_GetType(brush)) {
+ case Gdip.BrushTypeTextureFill:
+ brush = Gdip.Brush_Clone(brush);
+ if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+ data.gdipFgBrush = brush;
+ }
+ }
+ } else {
+ int foreground = data.foreground;
+ int rgb = ((foreground >> 16) & 0xFF) | (foreground & 0xFF00) | ((foreground & 0xFF) << 16);
+ int color = Gdip.Color_new(data.alpha << 24 | rgb);
+ if (color is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ brush = Gdip.SolidBrush_new(color);
+ if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.Color_delete(color);
+ data.gdipFgBrush = brush;
+ }
+ if (pen !is 0) {
+ Gdip.Pen_SetBrush(pen, brush);
+ } else {
+ pen = data.gdipPen = Gdip.Pen_new(brush, width);
+ }
+ }
+ if ((state & LINE_WIDTH) !is 0) {
+ Gdip.Pen_SetWidth(pen, width);
+ switch (data.lineStyle) {
+ case DWT.LINE_CUSTOM:
+ state |= LINE_STYLE;
+ }
+ }
+ if ((state & LINE_STYLE) !is 0) {
+ float[] dashes = null;
+ float dashOffset = 0;
+ int dashStyle = Gdip.DashStyleSolid;
+ switch (data.lineStyle) {
+ case DWT.LINE_SOLID: break;
+ case DWT.LINE_DOT: dashStyle = Gdip.DashStyleDot; if (width is 0) dashes = LINE_DOT_ZERO; break;
+ case DWT.LINE_DASH: dashStyle = Gdip.DashStyleDash; if (width is 0) dashes = LINE_DASH_ZERO; break;
+ case DWT.LINE_DASHDOT: dashStyle = Gdip.DashStyleDashDot; if (width is 0) dashes = LINE_DASHDOT_ZERO; break;
+ case DWT.LINE_DASHDOTDOT: dashStyle = Gdip.DashStyleDashDotDot; if (width is 0) dashes = LINE_DASHDOTDOT_ZERO; break;
+ case DWT.LINE_CUSTOM: {
+ if (data.lineDashes !is null) {
+ dashOffset = data.lineDashesOffset / Math.max (1, width);
+ dashes = new float[data.lineDashes.length * 2];
+ for (int i = 0; i < data.lineDashes.length; i++) {
+ float dash = data.lineDashes[i] / Math.max (1, width);
+ dashes[i] = dash;
+ dashes[i + data.lineDashes.length] = dash;
+ }
+ }
+ }
+ }
+ if (dashes !is null) {
+ Gdip.Pen_SetDashPattern(pen, dashes, dashes.length);
+ Gdip.Pen_SetDashStyle(pen, Gdip.DashStyleCustom);
+ Gdip.Pen_SetDashOffset(pen, dashOffset);
+ } else {
+ Gdip.Pen_SetDashStyle(pen, dashStyle);
+ }
+ }
+ if ((state & LINE_MITERLIMIT) !is 0) {
+ Gdip.Pen_SetMiterLimit(pen, data.lineMiterLimit);
+ }
+ if ((state & LINE_JOIN) !is 0) {
+ int joinStyle = 0;
+ switch (data.lineJoin) {
+ case DWT.JOIN_MITER: joinStyle = Gdip.LineJoinMiter; break;
+ case DWT.JOIN_BEVEL: joinStyle = Gdip.LineJoinBevel; break;
+ case DWT.JOIN_ROUND: joinStyle = Gdip.LineJoinRound; break;
+ }
+ Gdip.Pen_SetLineJoin(pen, joinStyle);
+ }
+ if ((state & LINE_CAP) !is 0) {
+ int dashCap = Gdip.DashCapFlat, capStyle = 0;
+ switch (data.lineCap) {
+ case DWT.CAP_FLAT: capStyle = Gdip.LineCapFlat; break;
+ case DWT.CAP_ROUND: capStyle = Gdip.LineCapRound; dashCap = Gdip.DashCapRound; break;
+ case DWT.CAP_SQUARE: capStyle = Gdip.LineCapSquare; break;
+ }
+ Gdip.Pen_SetLineCap(pen, capStyle, capStyle, dashCap);
+ }
+ if ((state & BACKGROUND) !is 0) {
+ if (data.gdipBgBrush !is 0) Gdip.SolidBrush_delete(data.gdipBgBrush);
+ data.gdipBgBrush = 0;
+ Pattern pattern = data.backgroundPattern;
+ if (pattern !is null) {
+ data.gdipBrush = pattern.handle;
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ switch (Gdip.Brush_GetType(data.gdipBrush)) {
+ case Gdip.BrushTypeTextureFill:
+ int brush = Gdip.Brush_Clone(data.gdipBrush);
+ if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+ data.gdipBrush = data.gdipBgBrush = brush;
+ }
+ }
+ } else {
+ int background = data.background;
+ int rgb = ((background >> 16) & 0xFF) | (background & 0xFF00) | ((background & 0xFF) << 16);
+ int color = Gdip.Color_new(data.alpha << 24 | rgb);
+ if (color is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int brush = Gdip.SolidBrush_new(color);
+ if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.Color_delete(color);
+ data.gdipBrush = data.gdipBgBrush = brush;
+ }
+ }
+ if ((state & FONT) !is 0) {
+ OS.SelectObject(handle, data.hFont);
+ int font = createGdipFont(handle, data.hFont);
+ if (data.gdipFont !is 0) Gdip.Font_delete(data.gdipFont);
+ data.gdipFont = font;
+ }
+ if ((state & DRAW_OFFSET) !is 0) {
+ data.gdipXOffset = data.gdipYOffset = 0;
+ int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+ float[] elements = new float[6];
+ Gdip.Graphics_GetTransform(gdipGraphics, matrix);
+ Gdip.Matrix_GetElements(matrix, elements);
+ Gdip.Matrix_delete(matrix);
+ float scaling = elements[0];
+ if (scaling < 0) scaling = -scaling;
+ float penWidth = data.lineWidth * scaling;
+ if (penWidth is 0 || ((int)penWidth % 2) is 1) {
+ data.gdipXOffset = 0.5f / scaling;
+ }
+ scaling = elements[3];
+ if (scaling < 0) scaling = -scaling;
+ penWidth = data.lineWidth * scaling;
+ if (penWidth is 0 || ((int)penWidth % 2) is 1) {
+ data.gdipYOffset = 0.5f / scaling;
+ }
+ }
+ return;
+ }
+ if ((state & (FOREGROUND | LINE_CAP | LINE_JOIN | LINE_STYLE | LINE_WIDTH)) !is 0) {
+ int color = data.foreground;
+ int width = (int)data.lineWidth;
+ int[] dashes = null;
+ int lineStyle = OS.PS_SOLID;
+ switch (data.lineStyle) {
+ case DWT.LINE_SOLID: break;
+ case DWT.LINE_DASH: lineStyle = OS.PS_DASH; break;
+ case DWT.LINE_DOT: lineStyle = OS.PS_DOT; break;
+ case DWT.LINE_DASHDOT: lineStyle = OS.PS_DASHDOT; break;
+ case DWT.LINE_DASHDOTDOT: lineStyle = OS.PS_DASHDOTDOT; break;
+ case DWT.LINE_CUSTOM: {
+ if (data.lineDashes !is null) {
+ lineStyle = OS.PS_USERSTYLE;
+ dashes = new int[data.lineDashes.length];
+ for (int i = 0; i < dashes.length; i++) {
+ dashes[i] = (int)data.lineDashes[i];
+ }
+ }
+ break;
+ }
+ }
+ if ((state & LINE_STYLE) !is 0) {
+ OS.SetBkMode(handle, data.lineStyle is DWT.LINE_SOLID ? OS.OPAQUE : OS.TRANSPARENT);
+ }
+ int joinStyle = 0;
+ switch (data.lineJoin) {
+ case DWT.JOIN_MITER: joinStyle = OS.PS_JOIN_MITER; break;
+ case DWT.JOIN_ROUND: joinStyle = OS.PS_JOIN_ROUND; break;
+ case DWT.JOIN_BEVEL: joinStyle = OS.PS_JOIN_BEVEL; break;
+ }
+ int capStyle = 0;
+ switch (data.lineCap) {
+ case DWT.CAP_ROUND: capStyle = OS.PS_ENDCAP_ROUND; break;
+ case DWT.CAP_FLAT: capStyle = OS.PS_ENDCAP_FLAT; break;
+ case DWT.CAP_SQUARE: capStyle = OS.PS_ENDCAP_SQUARE;break;
+ }
+ int style = lineStyle | joinStyle | capStyle;
+ /*
+ * Feature in Windows. Windows does not honour line styles other then
+ * PS_SOLID for pens wider than 1 pixel created with CreatePen(). The fix
+ * is to use ExtCreatePen() instead.
+ */
+ int newPen;
+ if (OS.IsWinCE || (width is 0 && lineStyle !is OS.PS_USERSTYLE) || style is 0) {
+ newPen = OS.CreatePen(style & OS.PS_STYLE_MASK, width, color);
+ } else {
+ LOGBRUSH logBrush = new LOGBRUSH();
+ logBrush.lbStyle = OS.BS_SOLID;
+ logBrush.lbColor = color;
+ /* Feature in Windows. PS_GEOMETRIC pens cannot have zero width. */
+ newPen = OS.ExtCreatePen (style | OS.PS_GEOMETRIC, Math.max(1, width), logBrush, dashes !is null ? dashes.length : 0, dashes);
+ }
+ OS.SelectObject(handle, newPen);
+ data.state |= PEN;
+ data.state &= ~NULL_PEN;
+ if (data.hPen !is 0) OS.DeleteObject(data.hPen);
+ data.hPen = data.hOldPen = newPen;
+ } else if ((state & PEN) !is 0) {
+ OS.SelectObject(handle, data.hOldPen);
+ data.state &= ~NULL_PEN;
+ } else if ((state & NULL_PEN) !is 0) {
+ data.hOldPen = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
+ data.state &= ~PEN;
+ }
+ if ((state & BACKGROUND) !is 0) {
+ int newBrush = OS.CreateSolidBrush(data.background);
+ OS.SelectObject(handle, newBrush);
+ data.state |= BRUSH;
+ data.state &= ~NULL_BRUSH;
+ if (data.hBrush !is 0) OS.DeleteObject(data.hBrush);
+ data.hOldBrush = data.hBrush = newBrush;
+ } else if ((state & BRUSH) !is 0) {
+ OS.SelectObject(handle, data.hOldBrush);
+ data.state &= ~NULL_BRUSH;
+ } else if ((state & NULL_BRUSH) !is 0) {
+ data.hOldBrush = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
+ data.state &= ~BRUSH;
+ }
+ if ((state & BACKGROUND_TEXT) !is 0) {
+ OS.SetBkColor(handle, data.background);
+ }
+ if ((state & FOREGROUND_TEXT) !is 0) {
+ OS.SetTextColor(handle, data.foreground);
+ }
+ if ((state & FONT) !is 0) {
+ OS.SelectObject(handle, data.hFont);
+ }
+}
+
+/**
+ * Copies a rectangular area of the receiver at the specified
+ * position into the image, which must be of type DWT.BITMAP
.
+ *
+ * @param image the image to copy into
+ * @param x the x coordinate in the receiver of the area to be copied
+ * @param y the y coordinate in the receiver of the area to be copied
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void copyArea(Image image, int x, int y) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (image is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ if (image.type !is DWT.BITMAP || image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+
+ /* Copy the bitmap area */
+ Rectangle rect = image.getBounds();
+ int memHdc = OS.CreateCompatibleDC(handle);
+ int hOldBitmap = OS.SelectObject(memHdc, image.handle);
+ OS.BitBlt(memHdc, 0, 0, rect.width, rect.height, handle, x, y, OS.SRCCOPY);
+ OS.SelectObject(memHdc, hOldBitmap);
+ OS.DeleteDC(memHdc);
+}
+
+/**
+ * Copies a rectangular area of the receiver at the source
+ * position onto the receiver at the destination position.
+ *
+ * @param srcX the x coordinate in the receiver of the area to be copied
+ * @param srcY the y coordinate in the receiver of the area to be copied
+ * @param width the width of the area to copy
+ * @param height the height of the area to copy
+ * @param destX the x coordinate in the receiver of the area to copy to
+ * @param destY the y coordinate in the receiver of the area to copy to
+ *
+ * @exception DWTException
+ *
+ */
+public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY) {
+ copyArea(srcX, srcY, width, height, destX, destY, true);
+}
+
+/**
+ * Copies a rectangular area of the receiver at the source
+ * position onto the receiver at the destination position.
+ *
+ * @param srcX the x coordinate in the receiver of the area to be copied
+ * @param srcY the y coordinate in the receiver of the area to be copied
+ * @param width the width of the area to copy
+ * @param height the height of the area to copy
+ * @param destX the x coordinate in the receiver of the area to copy to
+ * @param destY the y coordinate in the receiver of the area to copy to
+ * @param paint if true
paint events will be generated for old and obscured areas
+ *
+ * @exception DWTException
+ *
+ *
+ * @since 3.1
+ */
+public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY, bool paint) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+
+ /*
+ * Feature in WinCE. The function WindowFromDC is not part of the
+ * WinCE SDK. The fix is to remember the HWND.
+ */
+ int hwnd = data.hwnd;
+ if (hwnd is 0) {
+ OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY);
+ } else {
+ RECT lprcClip = null;
+ int hrgn = OS.CreateRectRgn(0, 0, 0, 0);
+ if (OS.GetClipRgn(handle, hrgn) is 1) {
+ lprcClip = new RECT();
+ OS.GetRgnBox(hrgn, lprcClip);
+ }
+ OS.DeleteObject(hrgn);
+ RECT lprcScroll = new RECT();
+ OS.SetRect(lprcScroll, srcX, srcY, srcX + width, srcY + height);
+ int flags = paint ? OS.SW_INVALIDATE | OS.SW_ERASE : 0;
+ int res = OS.ScrollWindowEx(hwnd, destX - srcX, destY - srcY, lprcScroll, lprcClip, 0, null, flags);
+
+ /*
+ * Feature in WinCE. ScrollWindowEx does not accept combined
+ * vertical and horizontal scrolling. The fix is to do a
+ * BitBlt and invalidate the appropriate source area.
+ */
+ if (res is 0 && OS.IsWinCE) {
+ OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY);
+ if (paint) {
+ int deltaX = destX - srcX, deltaY = destY - srcY;
+ bool disjoint = (destX + width < srcX) || (srcX + width < destX) || (destY + height < srcY) || (srcY + height < destY);
+ if (disjoint) {
+ OS.InvalidateRect(hwnd, lprcScroll, true);
+ } else {
+ if (deltaX !is 0) {
+ int newX = destX - deltaX;
+ if (deltaX < 0) newX = destX + width;
+ OS.SetRect(lprcScroll, newX, srcY, newX + Math.abs(deltaX), srcY + height);
+ OS.InvalidateRect(hwnd, lprcScroll, true);
+ }
+ if (deltaY !is 0) {
+ int newY = destY - deltaY;
+ if (deltaY < 0) newY = destY + height;
+ OS.SetRect(lprcScroll, srcX, newY, srcX + width, newY + Math.abs(deltaY));
+ OS.InvalidateRect(hwnd, lprcScroll, true);
+ }
+ }
+ }
+ }
+ }
+}
+
+static int createGdipFont(int hDC, int hFont) {
+ int font = Gdip.Font_new(hDC, hFont);
+ if (font is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ if (!Gdip.Font_IsAvailable(font)) {
+ Gdip.Font_delete(font);
+ LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA();
+ OS.GetObject(hFont, LOGFONT.sizeof, logFont);
+ int size = Math.abs(logFont.lfHeight);
+ int style = Gdip.FontStyleRegular;
+ if (logFont.lfWeight is 700) style |= Gdip.FontStyleBold;
+ if (logFont.lfItalic !is 0) style |= Gdip.FontStyleItalic;
+ char[] chars;
+ if (OS.IsUnicode) {
+ chars = ((LOGFONTW)logFont).lfFaceName;
+ } else {
+ chars = new char[OS.LF_FACESIZE];
+ byte[] bytes = ((LOGFONTA)logFont).lfFaceName;
+ OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length);
+ }
+ int index = 0;
+ while (index < chars.length) {
+ if (chars [index] is 0) break;
+ index++;
+ }
+ String name = new String (chars, 0, index);
+ if (Compatibility.equalsIgnoreCase(name, "Courier")) { //$NON-NLS-1$
+ name = "Courier New"; //$NON-NLS-1$
+ }
+ char[] buffer = new char[name.length() + 1];
+ name.getChars(0, name.length(), buffer, 0);
+ font = Gdip.Font_new(buffer, size, style, Gdip.UnitPixel, 0);
+ }
+ if (font is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ return font;
+}
+
+static void destroyGdipBrush(int brush) {
+ int type = Gdip.Brush_GetType(brush);
+ switch (type) {
+ case Gdip.BrushTypeSolidColor:
+ Gdip.SolidBrush_delete(brush);
+ break;
+ case Gdip.BrushTypeHatchFill:
+ Gdip.HatchBrush_delete(brush);
+ break;
+ case Gdip.BrushTypeLinearGradient:
+ Gdip.LinearGradientBrush_delete(brush);
+ break;
+ case Gdip.BrushTypeTextureFill:
+ Gdip.TextureBrush_delete(brush);
+ break;
+ }
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the graphics context. Applications must dispose of all GCs
+ * which they allocate.
+ *
+ * @exception DWTError
+ *
+ */
+public void dispose() {
+ if (handle is 0) return;
+ if (data.device.isDisposed()) return;
+
+ disposeGdip();
+
+ /* Select stock pen and brush objects and free resources */
+ if (data.hPen !is 0) {
+ OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
+ OS.DeleteObject(data.hPen);
+ data.hPen = 0;
+ }
+ if (data.hBrush !is 0) {
+ OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
+ OS.DeleteObject(data.hBrush);
+ data.hBrush = 0;
+ }
+
+ /*
+ * Put back the original bitmap into the device context.
+ * This will ensure that we have not left a bitmap
+ * selected in it when we delete the HDC.
+ */
+ int hNullBitmap = data.hNullBitmap;
+ if (hNullBitmap !is 0) {
+ OS.SelectObject(handle, hNullBitmap);
+ data.hNullBitmap = 0;
+ }
+ Image image = data.image;
+ if (image !is null) image.memGC = null;
+
+ /*
+ * Dispose the HDC.
+ */
+ Device device = data.device;
+ if (drawable !is null) drawable.internal_dispose_GC(handle, data);
+ drawable = null;
+ handle = 0;
+ data.image = null;
+ data.ps = null;
+ if (device.tracking) device.dispose_Object(this);
+ data.device = null;
+ data = null;
+}
+
+void disposeGdip() {
+ if (data.gdipPen !is 0) Gdip.Pen_delete(data.gdipPen);
+ if (data.gdipBgBrush !is 0) destroyGdipBrush(data.gdipBgBrush);
+ if (data.gdipFgBrush !is 0) destroyGdipBrush(data.gdipFgBrush);
+ if (data.gdipFont !is 0) Gdip.Font_delete(data.gdipFont);
+ if (data.gdipGraphics !is 0) Gdip.Graphics_delete(data.gdipGraphics);
+ data.gdipGraphics = data.gdipBrush = data.gdipBgBrush = data.gdipFgBrush =
+ data.gdipFont = data.gdipPen = 0;
+}
+
+/**
+ * Draws the outline of a circular or elliptical arc
+ * within the specified rectangular area.
+ * startAngle
and extends
+ * for arcAngle
degrees, using the current color.
+ * Angles are interpreted such that 0 degrees is at the 3 o'clock
+ * position. A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ * x
, y
) and whose size is specified by the
+ * width
and height
arguments.
+ * width + 1
pixels wide
+ * by height + 1
pixels tall.
+ *
+ *
+ */
+public void drawArc (int x, int y, int width, int height, int startAngle, int arcAngle) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ checkGC(DRAW);
+ if (width < 0) {
+ x = x + width;
+ width = -width;
+ }
+ if (height < 0) {
+ y = y + height;
+ height = -height;
+ }
+ if (width is 0 || height is 0 || arcAngle is 0) return;
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ if (width is height) {
+ Gdip.Graphics_DrawArc(gdipGraphics, data.gdipPen, x, y, width, height, -startAngle, -arcAngle);
+ } else {
+ int path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate);
+ if (path is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int matrix = Gdip.Matrix_new(width, 0, 0, height, x, y);
+ if (matrix is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.GraphicsPath_AddArc(path, 0, 0, 1, 1, -startAngle, -arcAngle);
+ Gdip.GraphicsPath_Transform(path, matrix);
+ Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path);
+ Gdip.Matrix_delete(matrix);
+ Gdip.GraphicsPath_delete(path);
+ }
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+ }
+ /*
+ * Feature in WinCE. The function Arc is not present in the
+ * WinCE SDK. The fix is to emulate arc drawing by using
+ * Polyline.
+ */
+ if (OS.IsWinCE) {
+ /* compute arc with a simple linear interpolation */
+ if (arcAngle < 0) {
+ startAngle += arcAngle;
+ arcAngle = -arcAngle;
+ }
+ if (arcAngle > 360) arcAngle = 360;
+ int[] points = new int[(arcAngle + 1) * 2];
+ int cteX = 2 * x + width;
+ int cteY = 2 * y + height;
+ int index = 0;
+ for (int i = 0; i <= arcAngle; i++) {
+ points[index++] = (Compatibility.cos(startAngle + i, width) + cteX) >> 1;
+ points[index++] = (cteY - Compatibility.sin(startAngle + i, height)) >> 1;
+ }
+ OS.Polyline(handle, points, points.length / 2);
+ } else {
+ int x1, y1, x2, y2,tmp;
+ bool isNegative;
+ if (arcAngle >= 360 || arcAngle <= -360) {
+ x1 = x2 = x + width;
+ y1 = y2 = y + height / 2;
+ } else {
+ isNegative = arcAngle < 0;
+
+ arcAngle = arcAngle + startAngle;
+ if (isNegative) {
+ // swap angles
+ tmp = startAngle;
+ startAngle = arcAngle;
+ arcAngle = tmp;
+ }
+ x1 = Compatibility.cos(startAngle, width) + x + width/2;
+ y1 = -1 * Compatibility.sin(startAngle, height) + y + height/2;
+
+ x2 = Compatibility.cos(arcAngle, width) + x + width/2;
+ y2 = -1 * Compatibility.sin(arcAngle, height) + y + height/2;
+ }
+ OS.Arc(handle, x, y, x + width + 1, y + height + 1, x1, y1, x2, y2);
+ }
+}
+
+/**
+ * Draws a rectangle, based on the specified arguments, which has
+ * the appearance of the platform's focus rectangle if the
+ * platform supports such a notion, and otherwise draws a simple
+ * rectangle in the receiver's foreground color.
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ *
+ * @exception DWTException
+ *
+ *
+ * @see #drawRectangle(int, int, int, int)
+ */
+public void drawFocus (int x, int y, int width, int height) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if ((data.uiState & OS.UISF_HIDEFOCUS) !is 0) return;
+ int hdc = handle, state = 0;
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ int clipRgn = 0;
+ Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
+ int rgn = Gdip.Region_new();
+ if (rgn is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.Graphics_GetClip(gdipGraphics, rgn);
+ if (!Gdip.Region_IsInfinite(rgn, gdipGraphics)) {
+ clipRgn = Gdip.Region_GetHRGN(rgn, gdipGraphics);
+ }
+ Gdip.Region_delete(rgn);
+ Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
+ float[] lpXform = null;
+ int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+ if (matrix is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ Gdip.Graphics_GetTransform(gdipGraphics, matrix);
+ if (!Gdip.Matrix_IsIdentity(matrix)) {
+ lpXform = new float[6];
+ Gdip.Matrix_GetElements(matrix, lpXform);
+ }
+ Gdip.Matrix_delete(matrix);
+ hdc = Gdip.Graphics_GetHDC(gdipGraphics);
+ state = OS.SaveDC(hdc);
+ OS.SetBkColor(hdc, data.background);
+ OS.SetTextColor(hdc, data.foreground);
+ if (lpXform !is null) {
+ OS.SetGraphicsMode(hdc, OS.GM_ADVANCED);
+ OS.SetWorldTransform(hdc, lpXform);
+ }
+ if (clipRgn !is 0) {
+ OS.SelectClipRgn(hdc, clipRgn);
+ OS.DeleteObject(clipRgn);
+ }
+ }
+ RECT rect = new RECT();
+ OS.SetRect(rect, x, y, x + width, y + height);
+ OS.DrawFocusRect(hdc, rect);
+ if (gdipGraphics !is 0) {
+ OS.RestoreDC(hdc, state);
+ Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc);
+ }
+}
+
+/**
+ * Draws the given image in the receiver at the specified
+ * coordinates.
+ *
+ * @param image the image to draw
+ * @param x the x coordinate of where to draw
+ * @param y the y coordinate of where to draw
+ *
+ * @exception IllegalArgumentException
+ *
+ *
+ * @exception DWTError
+ *
+ */
+public void drawImage(Image image, int x, int y) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (image is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+ if (image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+ drawImage(image, 0, 0, -1, -1, x, y, -1, -1, true);
+}
+
+/**
+ * Copies a rectangular area from the source image into a (potentially
+ * different sized) rectangular area in the receiver. If the source
+ * and destination areas are of differing sizes, then the source
+ * area will be stretched or shrunk to fit the destination area
+ * as it is copied. The copy fails if any part of the source rectangle
+ * lies outside the bounds of the source image, or if any of the width
+ * or height arguments are negative.
+ *
+ * @param image the source image
+ * @param srcX the x coordinate in the source image to copy from
+ * @param srcY the y coordinate in the source image to copy from
+ * @param srcWidth the width in pixels to copy from the source
+ * @param srcHeight the height in pixels to copy from the source
+ * @param destX the x coordinate in the destination to copy to
+ * @param destY the y coordinate in the destination to copy to
+ * @param destWidth the width in pixels of the destination rectangle
+ * @param destHeight the height in pixels of the destination rectangle
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ * @exception DWTError
+ *
+ */
+public void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (srcWidth is 0 || srcHeight is 0 || destWidth is 0 || destHeight is 0) return;
+ if (srcX < 0 || srcY < 0 || srcWidth < 0 || srcHeight < 0 || destWidth < 0 || destHeight < 0) {
+ DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (image is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+ if (image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+ drawImage(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, false);
+}
+
+void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+ if (data.gdipGraphics !is 0) {
+ //TODO - cache bitmap
+ int[] gdipImage = srcImage.createGdipImage();
+ int img = gdipImage[0];
+ int imgWidth = Gdip.Image_GetWidth(img);
+ int imgHeight = Gdip.Image_GetHeight(img);
+ if (simple) {
+ srcWidth = destWidth = imgWidth;
+ srcHeight = destHeight = imgHeight;
+ } else {
+ if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
+ DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+ }
+ simple = srcX is 0 && srcY is 0 &&
+ srcWidth is destWidth && destWidth is imgWidth &&
+ srcHeight is destHeight && destHeight is imgHeight;
+ }
+ Rect rect = new Rect();
+ rect.X = destX;
+ rect.Y = destY;
+ rect.Width = destWidth;
+ rect.Height = destHeight;
+ /*
+ * Note that if the wrap mode is not WrapModeTileFlipXY, the scaled image
+ * is translucent around the borders.
+ */
+ int attrib = Gdip.ImageAttributes_new();
+ Gdip.ImageAttributes_SetWrapMode(attrib, Gdip.WrapModeTileFlipXY);
+ if (data.alpha !is 0xFF) {
+ float[] matrix = new float[]{
+ 1,0,0,0,0,
+ 0,1,0,0,0,
+ 0,0,1,0,0,
+ 0,0,0,data.alpha / (float)0xFF,0,
+ 0,0,0,0,1,
+ };
+ Gdip.ImageAttributes_SetColorMatrix(attrib, matrix, Gdip.ColorMatrixFlagsDefault, Gdip.ColorAdjustTypeBitmap);
+ }
+ int gstate = 0;
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ gstate = Gdip.Graphics_Save(data.gdipGraphics);
+ Gdip.Graphics_ScaleTransform(data.gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_TranslateTransform(data.gdipGraphics, - 2 * destX - destWidth, 0, Gdip.MatrixOrderPrepend);
+ }
+ Gdip.Graphics_DrawImage(data.gdipGraphics, img, rect, srcX, srcY, srcWidth, srcHeight, Gdip.UnitPixel, attrib, 0, 0);
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ Gdip.Graphics_Restore(data.gdipGraphics, gstate);
+ }
+ Gdip.ImageAttributes_delete(attrib);
+ Gdip.Bitmap_delete(img);
+ if (gdipImage[1] !is 0) {
+ int hHeap = OS.GetProcessHeap ();
+ OS.HeapFree(hHeap, 0, gdipImage[1]);
+ }
+ return;
+ }
+ switch (srcImage.type) {
+ case DWT.BITMAP:
+ drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
+ break;
+ case DWT.ICON:
+ drawIcon(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
+ break;
+ }
+}
+
+void drawIcon(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+ int technology = OS.GetDeviceCaps(handle, OS.TECHNOLOGY);
+
+ bool drawIcon = true;
+ int flags = OS.DI_NORMAL;
+ int offsetX = 0, offsetY = 0;
+ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(5, 1)) {
+ if ((OS.GetLayout(handle) & OS.LAYOUT_RTL) !is 0) {
+ flags |= OS.DI_NOMIRROR;
+ /*
+ * Bug in Windows. For some reason, DrawIconEx() does not take
+ * into account the window origin when the DI_NOMIRROR and
+ * LAYOUT_RTL are set. The fix is to set the window origin to
+ * (0, 0) and offset the drawing ourselves.
+ */
+ POINT pt = new POINT();
+ OS.GetWindowOrgEx(handle, pt);
+ offsetX = pt.x;
+ offsetY = pt.y;
+ }
+ } else {
+ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+ drawIcon = (OS.GetLayout(handle) & OS.LAYOUT_RTL) is 0;
+ }
+ }
+
+ /* Simple case: no stretching, entire icon */
+ if (simple && technology !is OS.DT_RASPRINTER && drawIcon) {
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+ OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags);
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+ return;
+ }
+
+ /* Get the icon info */
+ ICONINFO srcIconInfo = new ICONINFO();
+ if (OS.IsWinCE) {
+ Image.GetIconInfo(srcImage, srcIconInfo);
+ } else {
+ OS.GetIconInfo(srcImage.handle, srcIconInfo);
+ }
+
+ /* Get the icon width and height */
+ int hBitmap = srcIconInfo.hbmColor;
+ if (hBitmap is 0) hBitmap = srcIconInfo.hbmMask;
+ BITMAP bm = new BITMAP();
+ OS.GetObject(hBitmap, BITMAP.sizeof, bm);
+ int iconWidth = bm.bmWidth, iconHeight = bm.bmHeight;
+ if (hBitmap is srcIconInfo.hbmMask) iconHeight /= 2;
+
+ if (simple) {
+ srcWidth = destWidth = iconWidth;
+ srcHeight = destHeight = iconHeight;
+ }
+
+ /* Draw the icon */
+ bool failed = srcX + srcWidth > iconWidth || srcY + srcHeight > iconHeight;
+ if (!failed) {
+ simple = srcX is 0 && srcY is 0 &&
+ srcWidth is destWidth && srcHeight is destHeight &&
+ srcWidth is iconWidth && srcHeight is iconHeight;
+ if (!drawIcon) {
+ drawBitmapMask(srcImage, srcIconInfo.hbmColor, srcIconInfo.hbmMask, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, iconWidth, iconHeight, false);
+ } else if (simple && technology !is OS.DT_RASPRINTER) {
+ /* Simple case: no stretching, entire icon */
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+ OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags);
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+ } else {
+ /* Create the icon info and HDC's */
+ ICONINFO newIconInfo = new ICONINFO();
+ newIconInfo.fIcon = true;
+ int srcHdc = OS.CreateCompatibleDC(handle);
+ int dstHdc = OS.CreateCompatibleDC(handle);
+
+ /* Blt the color bitmap */
+ int srcColorY = srcY;
+ int srcColor = srcIconInfo.hbmColor;
+ if (srcColor is 0) {
+ srcColor = srcIconInfo.hbmMask;
+ srcColorY += iconHeight;
+ }
+ int oldSrcBitmap = OS.SelectObject(srcHdc, srcColor);
+ newIconInfo.hbmColor = OS.CreateCompatibleBitmap(srcHdc, destWidth, destHeight);
+ if (newIconInfo.hbmColor is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int oldDestBitmap = OS.SelectObject(dstHdc, newIconInfo.hbmColor);
+ bool stretch = !simple && (srcWidth !is destWidth || srcHeight !is destHeight);
+ if (stretch) {
+ if (!OS.IsWinCE) OS.SetStretchBltMode(dstHdc, OS.COLORONCOLOR);
+ OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCCOPY);
+ } else {
+ OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCCOPY);
+ }
+
+ /* Blt the mask bitmap */
+ OS.SelectObject(srcHdc, srcIconInfo.hbmMask);
+ newIconInfo.hbmMask = OS.CreateBitmap(destWidth, destHeight, 1, 1, null);
+ if (newIconInfo.hbmMask is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ OS.SelectObject(dstHdc, newIconInfo.hbmMask);
+ if (stretch) {
+ OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCCOPY);
+ } else {
+ OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+ }
+
+ if (technology is OS.DT_RASPRINTER) {
+ OS.SelectObject(srcHdc, newIconInfo.hbmColor);
+ OS.SelectObject(dstHdc, newIconInfo.hbmMask);
+ drawBitmapTransparentByClipping(srcHdc, dstHdc, 0, 0, destWidth, destHeight, destX, destY, destWidth, destHeight, true, destWidth, destHeight);
+ OS.SelectObject(srcHdc, oldSrcBitmap);
+ OS.SelectObject(dstHdc, oldDestBitmap);
+ } else {
+ OS.SelectObject(srcHdc, oldSrcBitmap);
+ OS.SelectObject(dstHdc, oldDestBitmap);
+ int hIcon = OS.CreateIconIndirect(newIconInfo);
+ if (hIcon is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+ OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, hIcon, destWidth, destHeight, 0, 0, flags);
+ if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+ OS.DestroyIcon(hIcon);
+ }
+
+ /* Destroy the new icon src and mask and hdc's*/
+ OS.DeleteObject(newIconInfo.hbmMask);
+ OS.DeleteObject(newIconInfo.hbmColor);
+ OS.DeleteDC(dstHdc);
+ OS.DeleteDC(srcHdc);
+ }
+ }
+
+ /* Free icon info */
+ OS.DeleteObject(srcIconInfo.hbmMask);
+ if (srcIconInfo.hbmColor !is 0) {
+ OS.DeleteObject(srcIconInfo.hbmColor);
+ }
+
+ if (failed) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+}
+
+void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+ BITMAP bm = new BITMAP();
+ OS.GetObject(srcImage.handle, BITMAP.sizeof, bm);
+ int imgWidth = bm.bmWidth;
+ int imgHeight = bm.bmHeight;
+ if (simple) {
+ srcWidth = destWidth = imgWidth;
+ srcHeight = destHeight = imgHeight;
+ } else {
+ if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
+ DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+ }
+ simple = srcX is 0 && srcY is 0 &&
+ srcWidth is destWidth && destWidth is imgWidth &&
+ srcHeight is destHeight && destHeight is imgHeight;
+ }
+ bool mustRestore = false;
+ GC memGC = srcImage.memGC;
+ if (memGC !is null && !memGC.isDisposed()) {
+ memGC.flush();
+ mustRestore = true;
+ GCData data = memGC.data;
+ if (data.hNullBitmap !is 0) {
+ OS.SelectObject(memGC.handle, data.hNullBitmap);
+ data.hNullBitmap = 0;
+ }
+ }
+ if (srcImage.alpha !is -1 || srcImage.alphaData !is null) {
+ drawBitmapAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+ } else if (srcImage.transparentPixel !is -1) {
+ drawBitmapTransparent(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+ } else {
+ drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+ }
+ if (mustRestore) {
+ int hOldBitmap = OS.SelectObject(memGC.handle, srcImage.handle);
+ memGC.data.hNullBitmap = hOldBitmap;
+ }
+}
+
+void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, BITMAP bm, int imgWidth, int imgHeight) {
+ /* Simple cases */
+ if (srcImage.alpha is 0) return;
+ if (srcImage.alpha is 255) {
+ drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+ return;
+ }
+
+ if (OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+ BLENDFUNCTION blend = new BLENDFUNCTION();
+ blend.BlendOp = OS.AC_SRC_OVER;
+ int srcHdc = OS.CreateCompatibleDC(handle);
+ int oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle);
+ if (srcImage.alpha !is -1) {
+ blend.SourceConstantAlpha = (byte)srcImage.alpha;
+ OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, blend);
+ } else {
+ int memDib = Image.createDIB(srcWidth, srcHeight, 32);
+ if (memDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int memHdc = OS.CreateCompatibleDC(handle);
+ int oldMemBitmap = OS.SelectObject(memHdc, memDib);
+ BITMAP dibBM = new BITMAP();
+ OS.GetObject(memDib, BITMAP.sizeof, dibBM);
+ OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+ byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight];
+ OS.MoveMemory(srcData, dibBM.bmBits, srcData.length);
+ final int apinc = imgWidth - srcWidth;
+ int ap = srcY * imgWidth + srcX, sp = 0;
+ byte[] alphaData = srcImage.alphaData;
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int alpha = alphaData[ap++] & 0xff;
+ int r = ((srcData[sp + 0] & 0xFF) * alpha) + 128;
+ r = (r + (r >> 8)) >> 8;
+ int g = ((srcData[sp + 1] & 0xFF) * alpha) + 128;
+ g = (g + (g >> 8)) >> 8;
+ int b = ((srcData[sp + 2] & 0xFF) * alpha) + 128;
+ b = (b + (b >> 8)) >> 8;
+ srcData[sp+0] = (byte)r;
+ srcData[sp+1] = (byte)g;
+ srcData[sp+2] = (byte)b;
+ srcData[sp+3] = (byte)alpha;
+ sp += 4;
+ }
+ ap += apinc;
+ }
+ OS.MoveMemory(dibBM.bmBits, srcData, srcData.length);
+ blend.SourceConstantAlpha = (byte)0xff;
+ blend.AlphaFormat = OS.AC_SRC_ALPHA;
+ OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, blend);
+ OS.SelectObject(memHdc, oldMemBitmap);
+ OS.DeleteDC(memHdc);
+ OS.DeleteObject(memDib);
+ }
+ OS.SelectObject(srcHdc, oldSrcBitmap);
+ OS.DeleteDC(srcHdc);
+ return;
+ }
+
+ /* Check clipping */
+ Rectangle rect = getClipping();
+ rect = rect.intersection(new Rectangle(destX, destY, destWidth, destHeight));
+ if (rect.isEmpty()) return;
+
+ /*
+ * Optimization. Recalculate src and dest rectangles so that
+ * only the clipping area is drawn.
+ */
+ int sx1 = srcX + (((rect.x - destX) * srcWidth) / destWidth);
+ int sx2 = srcX + ((((rect.x + rect.width) - destX) * srcWidth) / destWidth);
+ int sy1 = srcY + (((rect.y - destY) * srcHeight) / destHeight);
+ int sy2 = srcY + ((((rect.y + rect.height) - destY) * srcHeight) / destHeight);
+ destX = rect.x;
+ destY = rect.y;
+ destWidth = rect.width;
+ destHeight = rect.height;
+ srcX = sx1;
+ srcY = sy1;
+ srcWidth = Math.max(1, sx2 - sx1);
+ srcHeight = Math.max(1, sy2 - sy1);
+
+ /* Create resources */
+ int srcHdc = OS.CreateCompatibleDC(handle);
+ int oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle);
+ int memHdc = OS.CreateCompatibleDC(handle);
+ int memDib = Image.createDIB(Math.max(srcWidth, destWidth), Math.max(srcHeight, destHeight), 32);
+ if (memDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int oldMemBitmap = OS.SelectObject(memHdc, memDib);
+
+ BITMAP dibBM = new BITMAP();
+ OS.GetObject(memDib, BITMAP.sizeof, dibBM);
+ int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
+
+ /* Get the background pixels */
+ OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY);
+ byte[] destData = new byte[sizeInBytes];
+ OS.MoveMemory(destData, dibBM.bmBits, sizeInBytes);
+
+ /* Get the foreground pixels */
+ OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+ byte[] srcData = new byte[sizeInBytes];
+ OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes);
+
+ /* Merge the alpha channel in place */
+ int alpha = srcImage.alpha;
+ final bool hasAlphaChannel = (srcImage.alpha is -1);
+ if (hasAlphaChannel) {
+ final int apinc = imgWidth - srcWidth;
+ final int spinc = dibBM.bmWidthBytes - srcWidth * 4;
+ int ap = srcY * imgWidth + srcX, sp = 3;
+ byte[] alphaData = srcImage.alphaData;
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ srcData[sp] = alphaData[ap++];
+ sp += 4;
+ }
+ ap += apinc;
+ sp += spinc;
+ }
+ }
+
+ /* Scale the foreground pixels with alpha */
+ OS.MoveMemory(dibBM.bmBits, srcData, sizeInBytes);
+ /*
+ * Bug in WinCE and Win98. StretchBlt does not correctly stretch when
+ * the source and destination HDCs are the same. The workaround is to
+ * stretch to a temporary HDC and blit back into the original HDC.
+ * Note that on WinCE StretchBlt correctly compresses the image when the
+ * source and destination HDCs are the same.
+ */
+ if ((OS.IsWinCE && (destWidth > srcWidth || destHeight > srcHeight)) || (!OS.IsWinNT && !OS.IsWinCE)) {
+ int tempHdc = OS.CreateCompatibleDC(handle);
+ int tempDib = Image.createDIB(destWidth, destHeight, 32);
+ if (tempDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int oldTempBitmap = OS.SelectObject(tempHdc, tempDib);
+ if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+ if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR);
+ OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
+ } else {
+ OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+ }
+ OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY);
+ OS.SelectObject(tempHdc, oldTempBitmap);
+ OS.DeleteObject(tempDib);
+ OS.DeleteDC(tempHdc);
+ } else {
+ if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+ if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR);
+ OS.StretchBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
+ } else {
+ OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+ }
+ }
+ OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes);
+
+ /* Compose the pixels */
+ final int dpinc = dibBM.bmWidthBytes - destWidth * 4;
+ int dp = 0;
+ for (int y = 0; y < destHeight; ++y) {
+ for (int x = 0; x < destWidth; ++x) {
+ if (hasAlphaChannel) alpha = srcData[dp + 3] & 0xff;
+ destData[dp] += ((srcData[dp] & 0xff) - (destData[dp] & 0xff)) * alpha / 255;
+ destData[dp + 1] += ((srcData[dp + 1] & 0xff) - (destData[dp + 1] & 0xff)) * alpha / 255;
+ destData[dp + 2] += ((srcData[dp + 2] & 0xff) - (destData[dp + 2] & 0xff)) * alpha / 255;
+ dp += 4;
+ }
+ dp += dpinc;
+ }
+
+ /* Draw the composed pixels */
+ OS.MoveMemory(dibBM.bmBits, destData, sizeInBytes);
+ OS.BitBlt(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+
+ /* Free resources */
+ OS.SelectObject(memHdc, oldMemBitmap);
+ OS.DeleteDC(memHdc);
+ OS.DeleteObject(memDib);
+ OS.SelectObject(srcHdc, oldSrcBitmap);
+ OS.DeleteDC(srcHdc);
+}
+
+void drawBitmapTransparentByClipping(int srcHdc, int maskHdc, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight) {
+ /* Create a clipping region from the mask */
+ int rgn = OS.CreateRectRgn(0, 0, 0, 0);
+ for (int y=0; yx1
, y1
) and (x2
, y2
).
+ *
+ * @param x1 the first point's x coordinate
+ * @param y1 the first point's y coordinate
+ * @param x2 the second point's x coordinate
+ * @param y2 the second point's y coordinate
+ *
+ * @exception DWTException
+ *
+ */
+public void drawLine (int x1, int y1, int x2, int y2) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawLine(gdipGraphics, data.gdipPen, x1, y1, x2, y2);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+ x1--;
+ x2--;
+ }
+ }
+ if (OS.IsWinCE) {
+ int [] points = new int [] {x1, y1, x2, y2};
+ OS.Polyline (handle, points, points.length / 2);
+ } else {
+ OS.MoveToEx (handle, x1, y1, 0);
+ OS.LineTo (handle, x2, y2);
+ }
+ if (data.lineWidth <= 1) {
+ OS.SetPixel (handle, x2, y2, data.foreground);
+ }
+}
+
+/**
+ * Draws the outline of an oval, using the foreground color,
+ * within the specified rectangular area.
+ * x
, y
,
+ * width
, and height
arguments.
+ * width + 1
+ * pixels wide and height + 1
pixels tall.
+ *
+ *
+ */
+public void drawOval (int x, int y, int width, int height) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawEllipse(gdipGraphics, data.gdipPen, x, y, width, height);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+ }
+ OS.Ellipse(handle, x, y, x + width + 1, y + height + 1);
+}
+
+/**
+ * Draws the path described by the parameter.
+ *
+ *
+ * @exception DWTException
+ *
+ *
+ * @see Path
+ *
+ * @since 3.1
+ */
+public void drawPath (Path path) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (path is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ if (path.handle is 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+ initGdip();
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path.handle);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+}
+
+/**
+ * Draws a pixel, using the foreground color, at the specified
+ * point (x
, y
).
+ *
+ *
+ *
+ * @since 3.0
+ */
+public void drawPoint (int x, int y) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (data.gdipGraphics !is 0) {
+ checkGC(DRAW);
+ Gdip.Graphics_FillRectangle(data.gdipGraphics, getFgBrush(), x, y, 1, 1);
+ return;
+ }
+ OS.SetPixel (handle, x, y, data.foreground);
+}
+
+/**
+ * Draws the closed polygon which is defined by the specified array
+ * of integer coordinates, using the receiver's foreground color. The array
+ * contains alternating x and y values which are considered to represent
+ * points which are the vertices of the polygon. Lines are drawn between
+ * each consecutive pair, and between the first pair and last pair in the
+ * array.
+ *
+ * @param pointArray an array of alternating x and y values which are the vertices of the polygon
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawPolygon(int[] pointArray) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawPolygon(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+ for (int i = 0; i < pointArray.length; i+=2) {
+ pointArray[i]--;
+ }
+ }
+ }
+ OS.Polygon(handle, pointArray, pointArray.length / 2);
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+ for (int i = 0; i < pointArray.length; i+=2) {
+ pointArray[i]++;
+ }
+ }
+ }
+}
+
+/**
+ * Draws the polyline which is defined by the specified array
+ * of integer coordinates, using the receiver's foreground color. The array
+ * contains alternating x and y values which are considered to represent
+ * points which are the corners of the polyline. Lines are drawn between
+ * each consecutive pair, but not between the first pair and last pair in
+ * the array.
+ *
+ * @param pointArray an array of alternating x and y values which are the corners of the polyline
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawPolyline(int[] pointArray) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawLines(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+ for (int i = 0; i < pointArray.length; i+=2) {
+ pointArray[i]--;
+ }
+ }
+ }
+ OS.Polyline(handle, pointArray, pointArray.length / 2);
+ int length = pointArray.length;
+ if (length >= 2) {
+ if (data.lineWidth <= 1) {
+ OS.SetPixel (handle, pointArray[length - 2], pointArray[length - 1], data.foreground);
+ }
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+ for (int i = 0; i < pointArray.length; i+=2) {
+ pointArray[i]++;
+ }
+ }
+ }
+}
+
+/**
+ * Draws the outline of the rectangle specified by the arguments,
+ * using the receiver's foreground color. The left and right edges
+ * of the rectangle are at x
and x + width
.
+ * The top and bottom edges are at y
and y + height
.
+ *
+ * @param x the x coordinate of the rectangle to be drawn
+ * @param y the y coordinate of the rectangle to be drawn
+ * @param width the width of the rectangle to be drawn
+ * @param height the height of the rectangle to be drawn
+ *
+ * @exception DWTException
+ *
+ */
+public void drawRectangle (int x, int y, int width, int height) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ checkGC(DRAW);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_DrawRectangle(gdipGraphics, data.gdipPen, x, y, width, height);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ /*
+ * Note that Rectangle() subtracts one pixel in MIRRORED mode when
+ * the pen was created with CreatePen() and its width is 0 or 1.
+ */
+ if (data.lineWidth > 1) {
+ if ((data.lineWidth % 2) is 1) x++;
+ } else {
+ if (data.hPen !is 0 && OS.GetObject(data.hPen, 0, 0) !is LOGPEN.sizeof) {
+ x++;
+ }
+ }
+ }
+ OS.Rectangle (handle, x, y, x + width + 1, y + height + 1);
+}
+
+/**
+ * Draws the outline of the specified rectangle, using the receiver's
+ * foreground color. The left and right edges of the rectangle are at
+ * rect.x
and rect.x + rect.width
. The top
+ * and bottom edges are at rect.y
and
+ * rect.y + rect.height
.
+ *
+ * @param rect the rectangle to draw
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawRectangle (Rectangle rect) {
+ if (rect is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ drawRectangle (rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Draws the outline of the round-cornered rectangle specified by
+ * the arguments, using the receiver's foreground color. The left and
+ * right edges of the rectangle are at x
and x + width
.
+ * The top and bottom edges are at y
and y + height
.
+ * The roundness of the corners is specified by the
+ * arcWidth
and arcHeight
arguments, which
+ * are respectively the width and height of the ellipse used to draw
+ * the corners.
+ *
+ * @param x the x coordinate of the rectangle to be drawn
+ * @param y the y coordinate of the rectangle to be drawn
+ * @param width the width of the rectangle to be drawn
+ * @param height the height of the rectangle to be drawn
+ * @param arcWidth the width of the arc
+ * @param arcHeight the height of the arc
+ *
+ * @exception DWTException
+ *
+ */
+public void drawRoundRectangle (int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ checkGC(DRAW);
+ if (data.gdipGraphics !is 0) {
+ drawRoundRectangleGdip(data.gdipGraphics, data.gdipPen, x, y, width, height, arcWidth, arcHeight);
+ return;
+ }
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+ }
+ if (OS.IsWinCE) {
+ /*
+ * Bug in WinCE PPC. On certain devices, RoundRect does not draw
+ * all the pixels. The workaround is to draw a round rectangle
+ * using lines and arcs.
+ */
+ if (width is 0 || height is 0) return;
+ if (arcWidth is 0 || arcHeight is 0) {
+ drawRectangle(x, y, width, height);
+ return;
+ }
+ if (width < 0) {
+ x += width;
+ width = -width;
+ }
+ if (height < 0) {
+ y += height;
+ height = -height;
+ }
+ if (arcWidth < 0) arcWidth = -arcWidth;
+ if (arcHeight < 0) arcHeight = -arcHeight;
+ if (arcWidth > width) arcWidth = width;
+ if (arcHeight > height) arcHeight = height;
+
+ if (arcWidth < width) {
+ drawLine(x+arcWidth/2, y, x+width-arcWidth/2, y);
+ drawLine(x+arcWidth/2, y+height, x+width-arcWidth/2, y+height);
+ }
+ if (arcHeight < height) {
+ drawLine(x, y+arcHeight/2, x, y+height-arcHeight/2);
+ drawLine(x+width, y+arcHeight/2, x+width, y+height-arcHeight/2);
+ }
+ if (arcWidth !is 0 && arcHeight !is 0) {
+ drawArc(x, y, arcWidth, arcHeight, 90, 90);
+ drawArc(x+width-arcWidth, y, arcWidth, arcHeight, 0, 90);
+ drawArc(x+width-arcWidth, y+height-arcHeight, arcWidth, arcHeight, 0, -90);
+ drawArc(x, y+height-arcHeight, arcWidth, arcHeight, 180, 90);
+ }
+ } else {
+ OS.RoundRect(handle, x,y,x+width+1,y+height+1, arcWidth, arcHeight);
+ }
+}
+
+void drawRoundRectangleGdip (int gdipGraphics, int pen, int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ int nx = x;
+ int ny = y;
+ int nw = width;
+ int nh = height;
+ int naw = arcWidth;
+ int nah = arcHeight;
+
+ if (nw < 0) {
+ nw = 0 - nw;
+ nx = nx - nw;
+ }
+ if (nh < 0) {
+ nh = 0 - nh;
+ ny = ny - nh;
+ }
+ if (naw < 0)
+ naw = 0 - naw;
+ if (nah < 0)
+ nah = 0 - nah;
+
+ Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+ int path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate);
+ if (path is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ if (nw > naw) {
+ if (nh > nah) {
+ Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nah, 0, -90);
+ Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nah, -90, -90);
+ Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, naw, nah, -180, -90);
+ Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny + nh - nah, naw, nah, -270, -90);
+ } else {
+ Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nh, -270, -180);
+ Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nh, -90, -180);
+ }
+ } else {
+ if (nh > nah) {
+ Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nah, 0, -180);
+ Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, nw, nah, -180, -180);
+ } else {
+ Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nh, 0, 360);
+ }
+ }
+ Gdip.GraphicsPath_CloseFigure(path);
+ Gdip.Graphics_DrawPath(gdipGraphics, pen, path);
+ Gdip.GraphicsPath_delete(path);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. No tab expansion or carriage return processing
+ * will be performed. The background of the rectangular area where
+ * the string is being drawn will be filled with the receiver's
+ * background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawString (String string, int x, int y) {
+ drawString(string, x, y, false);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. No tab expansion or carriage return processing
+ * will be performed. If isTransparent
is true
,
+ * then the background of the rectangular area where the string is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param isTransparent if true
the background will be transparent, otherwise it will be opaque
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawString (String string, int x, int y, bool isTransparent) {
+ if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+ if (string is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+// TCHAR buffer = new TCHAR (getCodePage(), string, false);
+ int length = string.length();
+ if (length is 0) return;
+ char[] buffer = new char [length];
+ string.getChars(0, length, buffer, 0);
+ int gdipGraphics = data.gdipGraphics;
+ if (gdipGraphics !is 0) {
+ checkGC(FONT | FOREGROUND | (isTransparent ? 0 : BACKGROUND));
+ PointF pt = new PointF();
+ int format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic());
+ int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces;
+ if ((data.style & DWT.MIRRORED) !is 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft;
+ Gdip.StringFormat_SetFormatFlags(format, formatFlags);
+ if (!isTransparent) {
+ RectF bounds = new RectF();
+ Gdip.Graphics_MeasureString(gdipGraphics, buffer, length, data.gdipFont, pt, format, bounds);
+ Gdip.Graphics_FillRectangle(gdipGraphics, data.gdipBrush, x, y, Math.round(bounds.Width), Math.round(bounds.Height));
+ }
+ int gstate = 0, brush = getFgBrush();
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ switch (Gdip.Brush_GetType(brush)) {
+ case Gdip.BrushTypeLinearGradient:
+ Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+ Gdip.LinearGradientBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+ break;
+ case Gdip.BrushTypeTextureFill:
+ Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+ Gdip.TextureBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+ break;
+ }
+ gstate = Gdip.Graphics_Save(gdipGraphics);
+ Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
+ Gdip.Graphics_TranslateTransform(gdipGraphics, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+ }
+ pt.X = x;
+ pt.Y = y;
+ Gdip.Graphics_DrawString(gdipGraphics, buffer, length, data.gdipFont, pt, format, brush);
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ switch (Gdip.Brush_GetType(brush)) {
+ case Gdip.BrushTypeLinearGradient:
+ Gdip.LinearGradientBrush_ResetTransform(brush);
+ break;
+ case Gdip.BrushTypeTextureFill:
+ Gdip.TextureBrush_ResetTransform(brush);
+ break;
+ }
+ Gdip.Graphics_Restore(gdipGraphics, gstate);
+ }
+ Gdip.StringFormat_delete(format);
+ return;
+ }
+ int rop2 = 0;
+ if (OS.IsWinCE) {
+ rop2 = OS.SetROP2(handle, OS.R2_COPYPEN);
+ OS.SetROP2(handle, rop2);
+ } else {
+ rop2 = OS.GetROP2(handle);
+ }
+ checkGC(FONT | FOREGROUND_TEXT | BACKGROUND_TEXT);
+ int oldBkMode = OS.SetBkMode(handle, isTransparent ? OS.TRANSPARENT : OS.OPAQUE);
+ RECT rect = null;
+ SIZE size = null;
+ int flags = 0;
+ if ((data.style & DWT.MIRRORED) !is 0) {
+ if (!isTransparent) {
+ size = new SIZE();
+ OS.GetTextExtentPoint32W(handle, buffer, length, size);
+ rect = new RECT ();
+ rect.left = x;
+ rect.right = x + size.cx;
+ rect.top = y;
+ rect.bottom = y + size.cy;
+ flags = OS.ETO_CLIPPED;
+ }
+ x--;
+ }
+ if (rop2 !is OS.R2_XORPEN) {
+ OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null);
+ } else {
+ int foreground = OS.GetTextColor(handle);
+ if (isTransparent) {
+ if (size is null) {
+ size = new SIZE();
+ OS.GetTextExtentPoint32W(handle, buffer, length, size);
+ }
+ int width = size.cx, height = size.cy;
+ int hBitmap = OS.CreateCompatibleBitmap(handle, width, height);
+ if (hBitmap is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ int memDC = OS.CreateCompatibleDC(handle);
+ int hOldBitmap = OS.SelectObject(memDC, hBitmap);
+ OS.PatBlt(memDC, 0, 0, width, height, OS.BLACKNESS);
+ OS.SetBkMode(memDC, OS.TRANSPARENT);
+ OS.SetTextColor(memDC, foreground);
+ OS.SelectObject(memDC, OS.GetCurrentObject(handle, OS.OBJ_FONT));
+ OS.ExtTextOutW(memDC, 0, 0, 0, null, buffer, length, null);
+ OS.BitBlt(handle, x, y, width, height, memDC, 0, 0, OS.SRCINVERT);
+ OS.SelectObject(memDC, hOldBitmap);
+ OS.DeleteDC(memDC);
+ OS.DeleteObject(hBitmap);
+ } else {
+ int background = OS.GetBkColor(handle);
+ OS.SetTextColor(handle, foreground ^ background);
+ OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null);
+ OS.SetTextColor(handle, foreground);
+ }
+ }
+ OS.SetBkMode(handle, oldBkMode);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion and carriage return processing
+ * are performed. The background of the rectangular area where
+ * the text is being drawn will be filled with the receiver's
+ * background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawText (String string, int x, int y) {
+ drawText(string, x, y, DWT.DRAW_DELIMITER | DWT.DRAW_TAB);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion and carriage return processing
+ * are performed. If isTransparent
is true
,
+ * then the background of the rectangular area where the text is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param isTransparent if true
the background will be transparent, otherwise it will be opaque
+ *
+ * @exception IllegalArgumentException
+ *
+ * @exception DWTException
+ *
+ */
+public void drawText (String string, int x, int y, bool isTransparent) {
+ int flags = DWT.DRAW_DELIMITER | DWT.DRAW_TAB;
+ if (isTransparent) flags |= DWT.DRAW_TRANSPARENT;
+ drawText(string, x, y, flags);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion, line delimiter and mnemonic
+ * processing are performed according to the specified flags. If
+ * flags
includes DRAW_TRANSPARENT
,
+ * then the background of the rectangular area where the text is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ * flags
may be a combination of:
+ *
+ *
+ *
true
if the object is the same as this object and false
otherwise
+ *
+ * @see #hashCode
+ */
+public bool equals (Object object) {
+ return (object is this) || ((object instanceof GC) && (handle is ((GC)object).handle));
+}
+
+/**
+ * Fills the interior of a circular or elliptical arc within
+ * the specified rectangular area, with the receiver's background
+ * color.
+ *
+ * The resulting arc begins at startAngle
and extends
+ * for arcAngle
degrees, using the current color.
+ * Angles are interpreted such that 0 degrees is at the 3 o'clock
+ * position. A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ *
+ * The center of the arc is the center of the rectangle whose origin
+ * is (x
, y
) and whose size is specified by the
+ * width
and height
arguments.
+ *
+ * The resulting arc covers an area width + 1
pixels wide
+ * by height + 1
pixels tall.
+ *
+ * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param path the path to fill + * + * @exception IllegalArgumentException+ * The advance width is defined as the horizontal distance the cursor + * should move after printing the character in the selected font. + *
+ * + * @param ch the character to measure + * @return the distance in the x direction to move past the character before painting the next + * + * @exception DWTExceptiontrue
if receiver is using the operating system's
+ * advanced graphics subsystem. Otherwise, false
is returned
+ * to indicate that normal graphics are in use.
+ *
+ * Advanced graphics may not be installed for the operating system. In this
+ * case, false
is always returned. Some operating system have
+ * only one graphics subsystem. If this subsystem supports advanced graphics,
+ * then true
is always returned. If any graphics operation such
+ * as alpha, antialias, patterns, interpolation, paths, clipping or transformation
+ * has caused the receiver to switch from regular to advanced graphics mode,
+ * true
is returned. If the receiver has been explicitly switched
+ * to advanced mode and this mode is supported, true
is returned.
+ *
DWT.DEFAULT
, DWT.OFF
or
+ * DWT.ON
. Note that this controls anti-aliasing for all
+ * non-text drawing operations.
+ *
+ * @return the anti-aliasing setting
+ *
+ * @exception DWTException null
.
+ *
+ * @return the receiver's background pattern
+ *
+ * @exception DWTException + * The width is defined as the space taken up by the actual + * character, not including the leading and tailing whitespace + * or overhang. + *
+ * + * @param ch the character to measure + * @return the width of the character + * + * @exception DWTExceptionDWT.FILL_EVEN_ODD
or DWT.FILL_WINDING
.
+ *
+ * @return the receiver's fill rule
+ *
+ * @exception DWTException null
.
+ *
+ * @return the receiver's foreground pattern
+ *
+ * @exception DWTException
+ * IMPORTANT: This method is not part of the public
+ * API for GC
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
DWT.DEFAULT
, DWT.NONE
,
+ * DWT.LOW
or DWT.HIGH
.
+ *
+ * @return the receiver's interpolation setting
+ *
+ * @exception DWTException DWT.CAP_FLAT
, DWT.CAP_ROUND
,
+ * or DWT.CAP_SQUARE
.
+ *
+ * @return the cap style used for drawing lines
+ *
+ * @exception DWTException null
.
+ *
+ * @return the line dash style used for drawing lines
+ *
+ * @exception DWTException DWT.JOIN_MITER
, DWT.JOIN_ROUND
,
+ * or DWT.JOIN_BEVEL
.
+ *
+ * @return the join style used for drawing lines
+ *
+ * @exception DWTException DWT.LINE_SOLID
, DWT.LINE_DASH
,
+ * DWT.LINE_DOT
, DWT.LINE_DASHDOT
or
+ * DWT.LINE_DASHDOTDOT
.
+ *
+ * @return the style used for drawing lines
+ *
+ * @exception DWTException drawLine
, drawRectangle
,
+ * drawPolyline
, and so forth.
+ *
+ * @return the receiver's line width
+ *
+ * @exception DWTException + * Note that the value which is returned by this method may + * not match the value which was provided to the constructor + * when the receiver was created. This can occur when the underlying + * operating system does not support a particular combination of + * requested styles. + *
+ * + * @return the style bits + * + * @exception DWTExceptionDWT.DEFAULT
, DWT.OFF
or
+ * DWT.ON
. Note that this controls anti-aliasing
+ * only for text drawing operations.
+ *
+ * @return the anti-aliasing setting
+ *
+ * @exception DWTException true
if this GC is drawing in the mode
+ * where the resulting color in the destination is the
+ * exclusive or of the color values in the source
+ * and the destination, and false
if it is
+ * drawing in the mode where the destination color is being
+ * replaced with the source color value.
+ *
+ * @return true
true if the receiver is in XOR mode, and false otherwise
+ *
+ * @exception DWTException true
when passed to
+ * equals
must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @exception DWTException true
if the receiver has a clipping
+ * region set into it, and false
otherwise.
+ * If this method returns false, the receiver will draw on all
+ * available space in the destination. If it returns true,
+ * it will draw only in the area that is covered by the region
+ * that can be accessed with getClipping(region)
.
+ *
+ * @return true
if the GC has a clipping region, and false
otherwise
+ *
+ * @exception DWTException true
if the GC has been disposed,
+ * and false
otherwise.
+ *
+ * This method gets the dispose state for the GC.
+ * When a GC has been disposed, it is an error to
+ * invoke any other method using the GC.
+ *
+ * @return true
when the GC is disposed and false
otherwise
+ */
+public bool isDisposed() {
+ return handle is 0;
+}
+
+float measureSpace(int font, int format) {
+ PointF pt = new PointF();
+ RectF bounds = new RectF();
+ Gdip.Graphics_MeasureString(data.gdipGraphics, new char[]{' '}, 1, font, pt, format, bounds);
+ return bounds.Width;
+}
+
+/**
+ * Sets the receiver to always use the operating system's advanced graphics
+ * subsystem for all graphics operations if the argument is true
.
+ * If the argument is false
, the advanced graphics subsystem is
+ * no longer used, advanced graphics state is cleared and the normal graphics
+ * subsystem is used from now on.
+ *
+ * Normally, the advanced graphics subsystem is invoked automatically when + * any one of the alpha, antialias, patterns, interpolation, paths, clipping + * or transformation operations in the receiver is requested. When the receiver + * is switched into advanced mode, the advanced graphics subsystem performs both + * advanced and normal graphics operations. Because the two subsystems are + * different, their output may differ. Switching to advanced graphics before + * any graphics operations are performed ensures that the output is consistent. + *
+ * Advanced graphics may not be installed for the operating system. In this + * case, this operation does nothing. Some operating system have only one + * graphics subsystem, so switching from normal to advanced graphics does + * nothing. However, switching from advanced to normal graphics will always + * clear the advanced graphics state, even for operating systems that have + * only one graphics subsystem. + *
+ * + * @param advanced the new advanced graphics state + * + * @exception DWTExceptionDWT.DEFAULT
, DWT.OFF
+ * or DWT.ON
. Note that this controls anti-aliasing for all
+ * non-text drawing operations.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param antialias the anti-aliasing setting + * + * @exception IllegalArgumentExceptionDWT.DEFAULT
,
+ * DWT.OFF
or DWT.ON
+ * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * @param alpha the alpha value + * + * @exception DWTExceptionnull
.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param pattern the new background pattern + * + * @exception IllegalArgumentException+ * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param path the clipping path. + * + * @exception IllegalArgumentExceptionnull
for the
+ * rectangle reverts the receiver's clipping area to its
+ * original value.
+ *
+ * @param rect the clipping rectangle or null
+ *
+ * @exception DWTException null
for the
+ * region reverts the receiver's clipping area to its
+ * original value.
+ *
+ * @param region the clipping region or null
+ *
+ * @exception IllegalArgumentException DWT.FILL_EVEN_ODD
or DWT.FILL_WINDING
.
+ *
+ * @param rule the new fill rule
+ *
+ * @exception IllegalArgumentException DWT.FILL_EVEN_ODD
+ * or DWT.FILL_WINDING
null
.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * @param pattern the new foreground pattern + * + * @exception IllegalArgumentExceptionDWT.DEFAULT
, DWT.NONE
,
+ * DWT.LOW
or DWT.HIGH
.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param interpolation the new interpolation setting + * + * @exception IllegalArgumentExceptionDWT.DEFAULT
,
+ * DWT.NONE
, DWT.LOW
or DWT.HIGH
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * @param attributes the line attributes + * + * @exception IllegalArgumentExceptionDWT.CAP_FLAT
, DWT.CAP_ROUND
,
+ * or DWT.CAP_SQUARE
.
+ *
+ * @param cap the cap style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException null
. If the argument is not null
,
+ * the receiver's line style is set to DWT.LINE_CUSTOM
, otherwise
+ * it is set to DWT.LINE_SOLID
.
+ *
+ * @param dashes the dash style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException DWT.JOIN_MITER
, DWT.JOIN_ROUND
,
+ * or DWT.JOIN_BEVEL
.
+ *
+ * @param join the join style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException DWT.LINE_SOLID
, DWT.LINE_DASH
,
+ * DWT.LINE_DOT
, DWT.LINE_DASHDOT
or
+ * DWT.LINE_DASHDOTDOT
.
+ *
+ * @param lineStyle the style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException drawLine
, drawRectangle
,
+ * drawPolyline
, and so forth.
+ * + * Note that line width of zero is used as a hint to + * indicate that the fastest possible line drawing + * algorithms should be used. This means that the + * output may be different from line width one. + *
+ * + * @param lineWidth the width of a line + * + * @exception DWTExceptiontrue
, puts the receiver
+ * in a drawing mode where the resulting color in the destination
+ * is the exclusive or of the color values in the source
+ * and the destination, and if the argument is false
,
+ * puts the receiver in a drawing mode where the destination color
+ * is replaced with the source color value.
+ * + * Note that this mode in fundamentally unsupportable on certain + * platforms, notably Carbon (Mac OS X). Clients that want their + * code to run on all platforms need to avoid this method. + *
+ * + * @param xor iftrue
, then xor mode is used, otherwise source copy mode is used
+ *
+ * @exception DWTException DWT.DEFAULT
, DWT.OFF
+ * or DWT.ON
. Note that this controls anti-aliasing only
+ * for all text drawing operations.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param antialias the anti-aliasing setting + * + * @exception IllegalArgumentExceptionDWT.DEFAULT
,
+ * DWT.OFF
or DWT.ON
null
, the current transform is set to
+ * the identity transform.
+ * + * This operation requires the operating system's advanced + * graphics subsystem which may not be available on some + * platforms. + *
+ * + * @param transform the transform to set + * + * @exception IllegalArgumentException+ * The extent of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + *
+ * + * @param string the string to measure + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException+ * The extent of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + *
+ * + * @param string the string to measure + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException+ * The extent of a string is the width and height of + * the rectangular area it would cover if drawn in a particular + * font (in this case, the current font in the receiver). + *
+ * + * @param string the string to measure + * @param flags the flags specifying how to process the text + * @return a point containing the extent of the string + * + * @exception IllegalArgumentException
+ * IMPORTANT: This method is not part of the public
+ * API for GC
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
GC
+ */
+public static GC win32_new(Drawable drawable, GCData data) {
+ GC gc = new GC();
+ int hDC = drawable.internal_new_GC(data);
+ gc.device = data.device;
+ gc.init(drawable, data, hDC);
+ return gc;
+}
+
+/**
+ * Invokes platform specific functionality to wrap a graphics context.
+ *
+ * IMPORTANT: This method is not part of the public
+ * API for GC
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
GC
+ */
+public static GC win32_new(int hDC, GCData data) {
+ GC gc = new GC();
+ gc.device = data.device;
+ gc.init(null, data, hDC);
+ return gc;
+}
+
+}
+++/
\ No newline at end of file
diff -r d2e87572b721 -r 5f2e72114476 dwt/graphics/Image.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/Image.d Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,2140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.graphics.Image;
+
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.internal.gdip.Gdip;
+import dwt.internal.win32.OS;
+
+import dwt.graphics.Color;
+import dwt.graphics.Device;
+import dwt.graphics.Drawable;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Resource;
+
+
+import dwt.dwthelper.InputStream;
+import dwt.dwthelper.utils;
+
+import tango.text.convert.Format;
+//import tango.stdc.string;
+//import tango.stdc.stringz;
+
+/**
+ * Instances of this class are graphics which have been prepared
+ * for display on a specific device. That is, they are ready
+ * to paint using methods such as GC.drawImage()
+ * and display on widgets with, for example, Button.setImage()
.
+ *
+ * If loaded from a file format that supports it, an
+ * Image
may have transparency, meaning that certain
+ * pixels are specified as being transparent when drawn. Examples
+ * of file formats that support transparency are GIF and PNG.
+ *
+ * There are two primary ways to use Images
.
+ * The first is to load a graphic file from disk and create an
+ * Image
from it. This is done using an Image
+ * constructor, for example:
+ *
+ * Image i = new Image(device, "C:\\graphic.bmp"); + *+ * A graphic file may contain a color table specifying which + * colors the image was intended to possess. In the above example, + * these colors will be mapped to the closest available color in + * DWT. It is possible to get more control over the mapping of + * colors as the image is being created, using code of the form: + *
+ * ImageData data = new ImageData("C:\\graphic.bmp"); + * RGB[] rgbs = data.getRGBs(); + * // At this point, rgbs contains specifications of all + * // the colors contained within this image. You may + * // allocate as many of these colors as you wish by + * // using the Color constructor Color(RGB), then + * // create the image: + * Image i = new Image(device, data); + *+ *
+ * Applications which require even greater control over the image
+ * loading process should use the support provided in class
+ * ImageLoader
.
+ *
+ * Application code must explicitly invoke the Image.dispose()
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required.
+ *
DWT.BITMAP
, DWT.ICON
)
+ * + * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *
+ */ + public int type; + + /** + * the handle to the OS image resource + * (Warning: This field is platform dependent) + *+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *
+ */ + public HGDIOBJ handle; + + /** + * specifies the transparent pixel + */ + int transparentPixel = -1; + + /** + * the GC which is drawing on the image + */ + GC memGC; + + /** + * the alpha data for the image + */ + byte[] alphaData; + + /** + * the global alpha value to be used for every pixel + */ + int alpha = -1; + + /** + * the image data used to create this image if it is a + * icon. Used only in WinCE + */ + ImageData data; + + /** + * width of the image + */ + int width = -1; + + /** + * height of the image + */ + int height = -1; + + /** + * specifies the default scanline padding + */ + static const int DEFAULT_SCANLINE_PAD = 4; + +/** + * Prevents uninitialized instances from being created outside the package. + */ +this () { +} + +/** + * Constructs an empty instance of this class with the + * specified width and height. The result may be drawn upon + * by creating a GC and using any of its drawing operations, + * as shown in the following example: + *+ * Image i = new Image(device, width, height); + * GC gc = new GC(i); + * gc.drawRectangle(0, 0, 50, 50); + * gc.dispose(); + *+ *
+ * Note: Some platforms may have a limitation on the size + * of image that can be created (size depends on width, height, + * and depth). For example, Windows 95, 98, and ME do not allow + * images larger than 16M. + *
+ * + * @param device the device on which to create the image + * @param width the width of the new image + * @param height the height of the new image + * + * @exception IllegalArgumentExceptionIMAGE_COPY
, IMAGE_DISABLE
or IMAGE_GRAY
+ *
+ * @exception IllegalArgumentException IMAGE_COPY
, IMAGE_DISABLE
or IMAGE_GRAY
+ * Image i = new Image(device, boundsRectangle); + * GC gc = new GC(i); + * gc.drawRectangle(0, 0, 50, 50); + * gc.dispose(); + *+ *
+ * Note: Some platforms may have a limitation on the size + * of image that can be created (size depends on width, height, + * and depth). For example, Windows 95, 98, and ME do not allow + * images larger than 16M. + *
+ * + * @param device the device on which to create the image + * @param bounds a rectangle specifying the image's width and height (must not be null) + * + * @exception IllegalArgumentExceptionImageData
.
+ *
+ * @param device the device on which to create the image
+ * @param data the image data to create the image from (must not be null)
+ *
+ * @exception IllegalArgumentException DWT.ICON
, from the two given ImageData
+ * objects. The two images must be the same size. Pixel transparency
+ * in either image will be ignored.
+ * + * The mask image should contain white wherever the icon is to be visible, + * and black wherever the icon is to be transparent. In addition, + * the source image should contain black wherever the icon is to be + * transparent. + *
+ * + * @param device the device on which to create the icon + * @param source the color data for the icon + * @param mask the mask data for the icon + * + * @exception IllegalArgumentException
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * ImageLoader.load()
.
+ *
+ * This constructor may be used to load a resource as follows: + *
+ *+ * static Image loadImage (Display display, Class clazz, String string) { + * InputStream stream = clazz.getResourceAsStream (string); + * if (stream is null) return null; + * Image image = null; + * try { + * image = new Image (display, stream); + * } catch (DWTException ex) { + * } finally { + * try { + * stream.close (); + * } catch (IOException ex) {} + * } + * return image; + * } + *+ * + * @param device the device on which to create the image + * @param stream the input stream to load the image from + * + * @exception IllegalArgumentException
+ * This constructor is provided for convenience when loading + * a single image only. If the specified file contains + * multiple images, only the first one will be used. + * + * @param device the device on which to create the image + * @param filename the name of the file to load the image from + * + * @exception IllegalArgumentException
true
if the object is the same as this object and false
otherwise
+ *
+ * @see #hashCode
+ */
+public int opEquals (Object object) {
+ if (object is this) return true;
+ if (!(cast(Image)object)) return false;
+ Image image = cast(Image) object;
+ return device is image.device && handle is image.handle;
+}
+
+/**
+ * Returns the color to which to map the transparent pixel, or null if
+ * the receiver has no transparent pixel.
+ * + * There are certain uses of Images that do not support transparency + * (for example, setting an image into a button or label). In these cases, + * it may be desired to simulate transparency by using the background + * color of the widget to paint the transparent pixels of the image. + * Use this method to check which color will be used in these cases + * in place of transparency. This value may be set with setBackground(). + *
+ * + * @return the background color of the image, or null if there is no transparency in the image + * + * @exception DWTException
ImageData
based on the receiver
+ * Modifications made to this ImageData
will not
+ * affect the Image.
+ *
+ * @return an ImageData
containing the image's data and attributes
+ *
+ * @exception DWTException true
when passed to
+ * equals
must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @see #equals
+ */
+public hash_t toHash () {
+ return cast(hash_t)handle;
+}
+
+void init(Device device, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+ }
+ this.device = device;
+ type = DWT.BITMAP;
+ auto hDC = device.internal_new_GC(null);
+ handle = OS.CreateCompatibleBitmap(hDC, width, height);
+ /*
+ * Feature in Windows. CreateCompatibleBitmap() may fail
+ * for large images. The fix is to create a DIB section
+ * in that case.
+ */
+ if (handle is null) {
+ int bits = OS.GetDeviceCaps(hDC, OS.BITSPIXEL);
+ int planes = OS.GetDeviceCaps(hDC, OS.PLANES);
+ int depth = bits * planes;
+ if (depth < 16) depth = 16;
+ handle = createDIB(width, height, depth);
+ }
+ if (handle !is null) {
+ auto memDC = OS.CreateCompatibleDC(hDC);
+ auto hOldBitmap = OS.SelectObject(memDC, handle);
+ OS.PatBlt(memDC, 0, 0, width, height, OS.PATCOPY);
+ OS.SelectObject(memDC, hOldBitmap);
+ OS.DeleteDC(memDC);
+ }
+ device.internal_dispose_GC(hDC, null);
+ if (handle is null) {
+ DWT.error(DWT.ERROR_NO_HANDLES, null, device.getLastError());
+ }
+}
+
+static HGDIOBJ createDIB(int width, int height, int depth) {
+ BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+ bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+ bmiHeader.biWidth = width;
+ bmiHeader.biHeight = -height;
+ bmiHeader.biPlanes = 1;
+ bmiHeader.biBitCount = cast(short)depth;
+ if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS;
+ else bmiHeader.biCompression = OS.BI_RGB;
+ byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)];
+ OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+ /* Set the rgb colors into the bitmap info */
+ if (OS.IsWinCE) {
+ int redMask = 0xFF00;
+ int greenMask = 0xFF0000;
+ int blueMask = 0xFF000000;
+ /* big endian */
+ int offset = BITMAPINFOHEADER.sizeof;
+ bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
+ bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
+ bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
+ bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
+ bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
+ bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
+ bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
+ bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
+ bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
+ bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
+ bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
+ bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
+ }
+
+ int[] pBits = new int[1];
+ return OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+}
+
+/**
+ * Feature in WinCE. GetIconInfo is not available in WinCE.
+ * The workaround is to cache the object ImageData for images
+ * of type DWT.ICON. The bitmaps hbmMask and hbmColor can then
+ * be reconstructed by using our version of getIconInfo.
+ * This function takes an ICONINFO object and sets the fields
+ * hbmMask and hbmColor with the corresponding bitmaps it has
+ * created.
+ * Note. These bitmaps must be freed - as they would have to be
+ * if the regular GetIconInfo had been used.
+ */
+static void GetIconInfo(Image image, ICONINFO* info) {
+ int[] result = init(image.device, null, image.data);
+ info.hbmColor = result[0];
+ info.hbmMask = result[1];
+}
+
+static int[] init(Device device, Image image, ImageData i) {
+ if (image !is null) image.device = device;
+
+ /*
+ * BUG in Windows 98:
+ * A monochrome DIBSection will display as solid black
+ * on Windows 98 machines, even though it contains the
+ * correct data. The fix is to convert 1-bit ImageData
+ * into 4-bit ImageData before creating the image.
+ */
+ /* Windows does not support 2-bit images. Convert to 4-bit image. */
+ if ((OS.IsWin95 && i.depth is 1 && i.getTransparencyType() !is DWT.TRANSPARENCY_MASK) || i.depth is 2) {
+ ImageData img = new ImageData(i.width, i.height, 4, i.palette);
+ ImageData.blit(ImageData.BLIT_SRC,
+ i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, null, null, null,
+ ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
+ img.data, img.depth, img.bytesPerLine, i.getByteOrder(), 0, 0, img.width, img.height, null, null, null,
+ false, false);
+ img.transparentPixel = i.transparentPixel;
+ img.maskPad = i.maskPad;
+ img.maskData = i.maskData;
+ img.alpha = i.alpha;
+ img.alphaData = i.alphaData;
+ i = img;
+ }
+ /*
+ * Windows supports 16-bit mask of (0x7C00, 0x3E0, 0x1F),
+ * 24-bit mask of (0xFF0000, 0xFF00, 0xFF) and 32-bit mask
+ * (0x00FF0000, 0x0000FF00, 0x000000FF) as documented in
+ * MSDN BITMAPINFOHEADER. Make sure the image is
+ * Windows-supported.
+ */
+ /*
+ * Note on WinCE. CreateDIBSection requires the biCompression
+ * field of the BITMAPINFOHEADER to be set to BI_BITFIELDS for
+ * 16 and 32 bit direct images (see MSDN for CreateDIBSection).
+ * In this case, the color mask can be set to any value. For
+ * consistency, it is set to the same mask used by non WinCE
+ * platforms in BI_RGB mode.
+ */
+ if (i.palette.isDirect) {
+ final PaletteData palette = i.palette;
+ final int redMask = palette.redMask;
+ final int greenMask = palette.greenMask;
+ final int blueMask = palette.blueMask;
+ int newDepth = i.depth;
+ int newOrder = ImageData.MSB_FIRST;
+ PaletteData newPalette = null;
+
+ switch (i.depth) {
+ case 8:
+ newDepth = 16;
+ newOrder = ImageData.LSB_FIRST;
+ newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+ break;
+ case 16:
+ newOrder = ImageData.LSB_FIRST;
+ if (!(redMask is 0x7C00 && greenMask is 0x3E0 && blueMask is 0x1F)) {
+ newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+ }
+ break;
+ case 24:
+ if (!(redMask is 0xFF && greenMask is 0xFF00 && blueMask is 0xFF0000)) {
+ newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+ }
+ break;
+ case 32:
+ if (!(redMask is 0xFF00 && greenMask is 0xFF0000 && blueMask is 0xFF000000)) {
+ newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+ }
+ break;
+ default:
+ DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+ }
+ if (newPalette !is null) {
+ ImageData img = new ImageData(i.width, i.height, newDepth, newPalette);
+ ImageData.blit(ImageData.BLIT_SRC,
+ i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, redMask, greenMask, blueMask,
+ ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
+ img.data, img.depth, img.bytesPerLine, newOrder, 0, 0, img.width, img.height, newPalette.redMask, newPalette.greenMask, newPalette.blueMask,
+ false, false);
+ if (i.transparentPixel !is -1) {
+ img.transparentPixel = newPalette.getPixel(palette.getRGB(i.transparentPixel));
+ }
+ img.maskPad = i.maskPad;
+ img.maskData = i.maskData;
+ img.alpha = i.alpha;
+ img.alphaData = i.alphaData;
+ i = img;
+ }
+ }
+ /* Construct bitmap info header by hand */
+ RGB[] rgbs = i.palette.getRGBs();
+ bool useBitfields = OS.IsWinCE && (i.depth is 16 || i.depth is 32);
+ BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+ bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+ bmiHeader.biWidth = i.width;
+ bmiHeader.biHeight = -i.height;
+ bmiHeader.biPlanes = 1;
+ bmiHeader.biBitCount = cast(short)i.depth;
+ if (useBitfields) bmiHeader.biCompression = OS.BI_BITFIELDS;
+ else bmiHeader.biCompression = OS.BI_RGB;
+ bmiHeader.biClrUsed = rgbs is null ? 0 : rgbs.length;
+ byte[] bmi;
+ if (i.palette.isDirect)
+ bmi = new byte[BITMAPINFOHEADER.sizeof + (useBitfields ? 12 : 0)];
+ else
+ bmi = new byte[BITMAPINFOHEADER.sizeof + rgbs.length * 4];
+ OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+ /* Set the rgb colors into the bitmap info */
+ int offset = BITMAPINFOHEADER.sizeof;
+ if (i.palette.isDirect) {
+ if (useBitfields) {
+ PaletteData palette = i.palette;
+ int redMask = palette.redMask;
+ int greenMask = palette.greenMask;
+ int blueMask = palette.blueMask;
+ /*
+ * The color masks must be written based on the
+ * endianness of the ImageData.
+ */
+ if (i.getByteOrder() is ImageData.LSB_FIRST) {
+ bmi[offset] = cast(byte)((redMask & 0xFF) >> 0);
+ bmi[offset + 1] = cast(byte)((redMask & 0xFF00) >> 8);
+ bmi[offset + 2] = cast(byte)((redMask & 0xFF0000) >> 16);
+ bmi[offset + 3] = cast(byte)((redMask & 0xFF000000) >> 24);
+ bmi[offset + 4] = cast(byte)((greenMask & 0xFF) >> 0);
+ bmi[offset + 5] = cast(byte)((greenMask & 0xFF00) >> 8);
+ bmi[offset + 6] = cast(byte)((greenMask & 0xFF0000) >> 16);
+ bmi[offset + 7] = cast(byte)((greenMask & 0xFF000000) >> 24);
+ bmi[offset + 8] = cast(byte)((blueMask & 0xFF) >> 0);
+ bmi[offset + 9] = cast(byte)((blueMask & 0xFF00) >> 8);
+ bmi[offset + 10] = cast(byte)((blueMask & 0xFF0000) >> 16);
+ bmi[offset + 11] = cast(byte)((blueMask & 0xFF000000) >> 24);
+ } else {
+ bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
+ bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
+ bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
+ bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
+ bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
+ bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
+ bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
+ bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
+ bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
+ bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
+ bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
+ bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
+ }
+ }
+ } else {
+ for (int j = 0; j < rgbs.length; j++) {
+ bmi[offset] = cast(byte)rgbs[j].blue;
+ bmi[offset + 1] = cast(byte)rgbs[j].green;
+ bmi[offset + 2] = cast(byte)rgbs[j].red;
+ bmi[offset + 3] = 0;
+ offset += 4;
+ }
+ }
+ int[] pBits = new int[1];
+ int hDib = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+ if (hDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ /* In case of a scanline pad other than 4, do the work to convert it */
+ byte[] data = i.data;
+ if (i.scanlinePad !is 4 && (i.bytesPerLine % 4 !is 0)) {
+ data = ImageData.convertPad(data, i.width, i.height, i.depth, i.scanlinePad, 4);
+ }
+ OS.MoveMemory(pBits[0], data, data.length);
+
+ int[] result = null;
+ if (i.getTransparencyType() is DWT.TRANSPARENCY_MASK) {
+ /* Get the HDC for the device */
+ auto hDC = device.internal_new_GC(null);
+
+ /* Create the color bitmap */
+ auto hdcSrc = OS.CreateCompatibleDC(hDC);
+ OS.SelectObject(hdcSrc, hDib);
+ int hBitmap = OS.CreateCompatibleBitmap(hDC, i.width, i.height);
+ if (hBitmap is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ auto hdcDest = OS.CreateCompatibleDC(hDC);
+ OS.SelectObject(hdcDest, hBitmap);
+ OS.BitBlt(hdcDest, 0, 0, i.width, i.height, hdcSrc, 0, 0, OS.SRCCOPY);
+
+ /* Release the HDC for the device */
+ device.internal_dispose_GC(hDC, null);
+
+ /* Create the mask. Windows requires icon masks to have a scanline pad of 2. */
+ byte[] maskData = ImageData.convertPad(i.maskData, i.width, i.height, 1, i.maskPad, 2);
+ int hMask = OS.CreateBitmap(i.width, i.height, 1, 1, maskData);
+ if (hMask is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ OS.SelectObject(hdcSrc, hMask);
+ OS.PatBlt(hdcSrc, 0, 0, i.width, i.height, OS.DSTINVERT);
+ OS.DeleteDC(hdcSrc);
+ OS.DeleteDC(hdcDest);
+ OS.DeleteObject(hDib);
+
+ if (image is null) {
+ result = [hBitmap, hMask];
+ } else {
+ /* Create the icon */
+ ICONINFO info = new ICONINFO();
+ info.fIcon = true;
+ info.hbmColor = hBitmap;
+ info.hbmMask = hMask;
+ int hIcon = OS.CreateIconIndirect(info);
+ if (hIcon is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+ OS.DeleteObject(hBitmap);
+ OS.DeleteObject(hMask);
+ image.handle = hIcon;
+ image.type = DWT.ICON;
+ if (OS.IsWinCE) image.data = i;
+ }
+ } else {
+ if (image is null) {
+ result = [hDib];
+ } else {
+ image.handle = hDib;
+ image.type = DWT.BITMAP;
+ image.transparentPixel = i.transparentPixel;
+ if (image.transparentPixel is -1) {
+ image.alpha = i.alpha;
+ if (i.alpha is -1 && i.alphaData !is null) {
+ int length = i.alphaData.length;
+ image.alphaData = new byte[length];
+ System.arraycopy(i.alphaData, 0, image.alphaData, 0, length);
+ }
+ }
+ }
+ }
+ return result;
+}
+
+static int[] init(Device device, Image image, ImageData source, ImageData mask) {
+ /* Create a temporary image and locate the black pixel */
+ ImageData imageData;
+ int blackIndex = 0;
+ if (source.palette.isDirect) {
+ imageData = new ImageData(source.width, source.height, source.depth, source.palette);
+ } else {
+ RGB black = new RGB(0, 0, 0);
+ RGB[] rgbs = source.getRGBs();
+ if (source.transparentPixel !is -1) {
+ /*
+ * The source had transparency, so we can use the transparent pixel
+ * for black.
+ */
+ RGB[] newRGBs = new RGB[rgbs.length];
+ System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length);
+ if (source.transparentPixel >= newRGBs.length) {
+ /* Grow the palette with black */
+ rgbs = new RGB[source.transparentPixel + 1];
+ System.arraycopy(newRGBs, 0, rgbs, 0, newRGBs.length);
+ for (int i = newRGBs.length; i <= source.transparentPixel; i++) {
+ rgbs[i] = new RGB(0, 0, 0);
+ }
+ } else {
+ newRGBs[source.transparentPixel] = black;
+ rgbs = newRGBs;
+ }
+ blackIndex = source.transparentPixel;
+ imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs));
+ } else {
+ while (blackIndex < rgbs.length) {
+ if (rgbs[blackIndex].equals(black)) break;
+ blackIndex++;
+ }
+ if (blackIndex is rgbs.length) {
+ /*
+ * We didn't find black in the palette, and there is no transparent
+ * pixel we can use.
+ */
+ if ((1 << source.depth) > rgbs.length) {
+ /* We can grow the palette and add black */
+ RGB[] newRGBs = new RGB[rgbs.length + 1];
+ System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length);
+ newRGBs[rgbs.length] = black;
+ rgbs = newRGBs;
+ } else {
+ /* No room to grow the palette */
+ blackIndex = -1;
+ }
+ }
+ imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs));
+ }
+ }
+ if (blackIndex is -1) {
+ /* There was no black in the palette, so just copy the data over */
+ System.arraycopy(source.data, 0, imageData.data, 0, imageData.data.length);
+ } else {
+ /* Modify the source image to contain black wherever the mask is 0 */
+ int[] imagePixels = new int[imageData.width];
+ int[] maskPixels = new int[mask.width];
+ for (int y = 0; y < imageData.height; y++) {
+ source.getPixels(0, y, imageData.width, imagePixels, 0);
+ mask.getPixels(0, y, mask.width, maskPixels, 0);
+ for (int i = 0; i < imagePixels.length; i++) {
+ if (maskPixels[i] is 0) imagePixels[i] = blackIndex;
+ }
+ imageData.setPixels(0, y, source.width, imagePixels, 0);
+ }
+ }
+ imageData.maskPad = mask.scanlinePad;
+ imageData.maskData = mask.data;
+ return init(device, image, imageData);
+}
+void init(Device device, ImageData i) {
+ if (i is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+ init(device, this, i);
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new GC handle.
+ *
+ * IMPORTANT: This method is not part of the public
+ * API for Image
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
+ * IMPORTANT: This method is not part of the public
+ * API for Image
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
true
if the image has been disposed,
+ * and false
otherwise.
+ *
+ * This method gets the dispose state for the image.
+ * When an image has been disposed, it is an error to
+ * invoke any other method using the image.
+ *
+ * @return true
when the image is disposed and false
otherwise
+ */
+public bool isDisposed() {
+ return handle is null;
+}
+
+/**
+ * Sets the color to which to map the transparent pixel.
+ *
+ * There are certain uses of Images
that do not support
+ * transparency (for example, setting an image into a button or label).
+ * In these cases, it may be desired to simulate transparency by using
+ * the background color of the widget to paint the transparent pixels
+ * of the image. This method specifies the color that will be used in
+ * these cases. For example:
+ *
+ * Button b = new Button(); + * image.setBackground(b.getBackground()); + * b.setImage(image); + *+ *
+ * The image may be modified by this operation (in effect, the + * transparent regions may be filled with the supplied color). Hence + * this operation is not reversible and it is not legal to call + * this function twice or with a null argument. + *
+ * This method has no effect if the receiver does not have a transparent + * pixel value. + *
+ * + * @param color the color to use when a transparent pixel is specified + * + * @exception IllegalArgumentException
+ * IMPORTANT: This method is not part of the public
+ * API for Image
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ *
DWT.BITMAP
or DWT.ICON
)
+ * @param handle the OS handle for the image
+ * @return a new image object containing the specified device, type and handle
+ */
+public static Image win32_new(Device device, int type, HGDIOBJ handle) {
+ if (device is null) device = Device.getDevice();
+ Image image = new Image();
+ image.type = type;
+ image.handle = handle;
+ image.device = device;
+ return image;
+}
+
+}
diff -r d2e87572b721 -r 5f2e72114476 dwt/graphics/ImageData.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageData.d Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,3653 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit Image
.
+ *
+ * Note that the public fields x
, y
,
+ * disposalMethod
and delayTime
are
+ * typically only used when the image is in a set of images used
+ * for animation.
+ *
+ * Note that a depth of 8 or less does not necessarily + * mean that the image is palette indexed, or + * conversely that a depth greater than 8 means that + * the image is direct color. Check the associated + * PaletteData's isDirect field for such determinations. + */ + public int depth; + + /** + * The scanline padding. + *
+ * If one scanline of the image is not a multiple of + * this number, it will be padded with zeros until it is. + *
+ */ + public int scanlinePad; + + /** + * The number of bytes per scanline. + *+ * This is a multiple of the scanline padding. + *
+ */ + public int bytesPerLine; + + /** + * The pixel data of the image. + *+ * Note that for 16 bit depth images the pixel data is stored + * in least significant byte order; however, for 24bit and + * 32bit depth images the pixel data is stored in most + * significant byte order. + *
+ */ + public byte[] data; + + /** + * The color table for the image. + */ + public PaletteData palette; + + /** + * The transparent pixel. + *+ * Pixels with this value are transparent. + *
+ * The default is -1 which means 'no transparent pixel'. + *
+ */ + public int transparentPixel; + + /** + * An icon-specific field containing the data from the icon mask. + *+ * This is a 1 bit bitmap stored with the most significant + * bit first. The number of bytes per scanline is + * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'. + *
+ * The default is null which means 'no transparency mask'. + *
+ */ + public byte[] maskData; + + /** + * An icon-specific field containing the scanline pad of the mask. + *+ * If one scanline of the transparency mask is not a + * multiple of this number, it will be padded with zeros until + * it is. + *
+ */ + public int maskPad; + + /** + * The alpha data of the image. + *+ * Every pixel can have an alpha blending value that + * varies from 0, meaning fully transparent, to 255 meaning + * fully opaque. The number of bytes per scanline is + * 'width'. + *
+ */ + public byte[] alphaData; + + /** + * The global alpha value to be used for every pixel. + *
+ * If this value is set, the alphaData
field
+ * is ignored and when the image is rendered each pixel
+ * will be blended with the background an amount
+ * proportional to this value.
+ *
+ * The default is -1 which means 'no global alpha value' + *
+ */ + public int alpha; + + /** + * The type of file from which the image was read. + * + * It is expressed as one of the following values: + *IMAGE_BMP
IMAGE_BMP_RLE
IMAGE_GIF
IMAGE_ICO
IMAGE_JPEG
IMAGE_PNG
DM_UNSPECIFIED
DM_FILL_NONE
DM_FILL_BACKGROUND
DM_FILL_PREVIOUS
ImageData
loaded from the specified
+ * input stream. Throws an error if an error occurs while loading
+ * the image, or if the image has an unsupported type. Application
+ * code is still responsible for closing the input stream.
+ *
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * ImageLoader.load()
.
+ *
+ * This constructor may be used to load a resource as follows: + *
+ *+ * static ImageData loadImageData (Class clazz, String string) { + * InputStream stream = clazz.getResourceAsStream (string); + * if (stream is null) return null; + * ImageData imageData = null; + * try { + * imageData = new ImageData (stream); + * } catch (DWTException ex) { + * } finally { + * try { + * stream.close (); + * } catch (IOException ex) {} + * } + * return imageData; + * } + *+ * + * @param stream the input stream to load the image from (must not be null) + * + * @exception IllegalArgumentException
ImageData
loaded from a file with the
+ * specified name. Throws an error if an error occurs loading the
+ * image, or if the image has an unsupported type.
+ *
+ * This constructor is provided for convenience when loading a single
+ * image only. If the file contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * ImageLoader.load()
.
+ *
+ * This method is for internal use, and is not described further. + *
+ */ +this( + int width, int height, int depth, PaletteData palette, + int scanlinePad, byte[] data, int maskPad, byte[] maskData, + byte[] alphaData, int alpha, int transparentPixel, int type, + int x, int y, int disposalMethod, int delayTime) +{ + if (palette is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); + if (!(depth is 1 || depth is 2 || depth is 4 || depth is 8 + || depth is 16 || depth is 24 || depth is 32)) { + DWT.error(DWT.ERROR_INVALID_ARGUMENT); + } + if (width <= 0 || height <= 0) { + DWT.error(DWT.ERROR_INVALID_ARGUMENT); + } + if (scanlinePad is 0) DWT.error (DWT.ERROR_CANNOT_BE_ZERO); + + int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1)) + / scanlinePad * scanlinePad; + setAllFields( + width, + height, + depth, + scanlinePad, + bytesPerLine, + data !is null ? data : new byte[bytesPerLine * height], + palette, + transparentPixel, + maskData, + maskPad, + alphaData, + alpha, + type, + x, + y, + disposalMethod, + delayTime); +} + +/** + * Initializes all fields in the receiver. This method must be called + * by all public constructors to ensure that all fields are initialized + * for a new ImageData object. If a new field is added to the class, + * then it must be added to this method. + *+ * This method is for internal use, and is not described further. + *
+ */ +void setAllFields(int width, int height, int depth, int scanlinePad, + int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel, + byte[] maskData, int maskPad, byte[] alphaData, int alpha, + int type, int x, int y, int disposalMethod, int delayTime) { + + this.width = width; + this.height = height; + this.depth = depth; + this.scanlinePad = scanlinePad; + this.bytesPerLine = bytesPerLine; + this.data = data; + this.palette = palette; + this.transparentPixel = transparentPixel; + this.maskData = maskData; + this.maskPad = maskPad; + this.alphaData = alphaData; + this.alpha = alpha; + this.type = type; + this.x = x; + this.y = y; + this.disposalMethod = disposalMethod; + this.delayTime = delayTime; +} + +/** + * Invokes internal DWT functionality to create a new instance of + * this class. + *
+ * IMPORTANT: This method is not part of the public
+ * API for ImageData
. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is subject
+ * to change without notice, and should never be called from
+ * application code.
+ *
+ * This method is for internal use, and is not described further. + *
+ */ +public static ImageData internal_new( + int width, int height, int depth, PaletteData palette, + int scanlinePad, byte[] data, int maskPad, byte[] maskData, + byte[] alphaData, int alpha, int transparentPixel, int type, + int x, int y, int disposalMethod, int delayTime) +{ + return new ImageData( + width, height, depth, palette, scanlinePad, data, maskPad, maskData, + alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime); +} + +ImageData colorMaskImage(int pixel) { + ImageData mask = new ImageData(width, height, 1, bwPalette(), + 2, null, 0, null, null, -1, -1, DWT.IMAGE_UNDEFINED, + 0, 0, 0, 0); + int[] row = new int[width]; + for (int y = 0; y < height; y++) { + getPixels(0, y, width, row, 0); + for (int i = 0; i < width; i++) { + if (pixel !is -1 && row[i] is pixel) { + row[i] = 0; + } else { + row[i] = 1; + } + } + mask.setPixels(0, y, width, row, 0); + } + return mask; +} + +static byte[] checkData(byte [] data) { + if (data is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); + return data; +} + +/** + * Returns a new instance of the same class as the receiver, + * whose slots have been filled in with copies of + * the values in the slots of the receiver. That is, the + * returned object is a deep copy of the receiver. + * + * @return a copy of the receiver. + */ +public Object clone() { + byte[] cloneData = new byte[data.length]; + System.arraycopy(data, 0, cloneData, 0, data.length); + byte[] cloneMaskData = null; + if (maskData !is null) { + cloneMaskData = new byte[maskData.length]; + System.arraycopy(maskData, 0, cloneMaskData, 0, maskData.length); + } + byte[] cloneAlphaData = null; + if (alphaData !is null) { + cloneAlphaData = new byte[alphaData.length]; + System.arraycopy(alphaData, 0, cloneAlphaData, 0, alphaData.length); + } + return new ImageData( + width, + height, + depth, + palette, + scanlinePad, + cloneData, + maskPad, + cloneMaskData, + cloneAlphaData, + alpha, + transparentPixel, + type, + x, + y, + disposalMethod, + delayTime); +} + +/** + * Returns the alpha value at offsetx
in
+ * scanline y
in the receiver's alpha data.
+ *
+ * @param x the x coordinate of the pixel to get the alpha value of
+ * @param y the y coordinate of the pixel to get the alpha value of
+ * @return the alpha value at the given coordinates
+ *
+ * @exception IllegalArgumentException getWidth
alpha values starting at offset
+ * x
in scanline y
in the receiver's alpha
+ * data starting at startIndex
.
+ *
+ * @param x the x position of the pixel to begin getting alpha values
+ * @param y the y position of the pixel to begin getting alpha values
+ * @param getWidth the width of the data to get
+ * @param alphas the buffer in which to put the alpha values
+ * @param startIndex the offset into the image to begin getting alpha values
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException x
in
+ * scanline y
in the receiver's data.
+ *
+ * @param x the x position of the pixel to get
+ * @param y the y position of the pixel to get
+ * @return the pixel at the given coordinates
+ *
+ * @exception IllegalArgumentException getWidth
pixel values starting at offset
+ * x
in scanline y
in the receiver's
+ * data starting at startIndex
.
+ *
+ * @param x the x position of the first pixel to get
+ * @param y the y position of the first pixel to get
+ * @param getWidth the width of the data to get
+ * @param pixels the buffer in which to put the pixels
+ * @param startIndex the offset into the byte array to begin storing pixels
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException getWidth
pixel values starting at offset
+ * x
in scanline y
in the receiver's
+ * data starting at startIndex
.
+ *
+ * @param x the x position of the first pixel to get
+ * @param y the y position of the first pixel to get
+ * @param getWidth the width of the data to get
+ * @param pixels the buffer in which to put the pixels
+ * @param startIndex the offset into the buffer to begin storing pixels
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException RGB
s which comprise the
+ * indexed color table of the receiver, or null if the receiver
+ * has a direct color model.
+ *
+ * @return the RGB values for the image or null if direct color
+ *
+ * @see PaletteData#getRGBs()
+ */
+public RGB[] getRGBs() {
+ return palette.getRGBs();
+}
+
+/**
+ * Returns an ImageData
which specifies the
+ * transparency mask information for the receiver. If the
+ * receiver has no transparency or is not an icon, returns
+ * an opaque mask.
+ *
+ * @return the transparency mask
+ */
+public ImageData getTransparencyMask() {
+ if (getTransparencyType() is DWT.TRANSPARENCY_MASK) {
+ return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
+ } else {
+ return colorMaskImage(transparentPixel);
+ }
+}
+
+/**
+ * Returns the image transparency type, which will be one of
+ * DWT.TRANSPARENCY_NONE
, DWT.TRANSPARENCY_MASK
,
+ * DWT.TRANSPARENCY_PIXEL
or DWT.TRANSPARENCY_ALPHA
.
+ *
+ * @return the receiver's transparency type
+ */
+public int getTransparencyType() {
+ if (maskData !is null) return DWT.TRANSPARENCY_MASK;
+ if (transparentPixel !is -1) return DWT.TRANSPARENCY_PIXEL;
+ if (alphaData !is null) return DWT.TRANSPARENCY_ALPHA;
+ return DWT.TRANSPARENCY_NONE;
+}
+
+/**
+ * Returns the byte order of the receiver.
+ *
+ * @return MSB_FIRST or LSB_FIRST
+ */
+int getByteOrder() {
+ return depth !is 16 ? MSB_FIRST : LSB_FIRST;
+}
+
+/**
+ * Returns a copy of the receiver which has been stretched or
+ * shrunk to the specified size. If either the width or height
+ * is negative, the resulting image will be inverted in the
+ * associated axis.
+ *
+ * @param width the width of the new ImageData
+ * @param height the height of the new ImageData
+ * @return a scaled copy of the image
+ */
+public ImageData scaledTo(int width, int height) {
+ /* Create a destination image with no data */
+ bool flipX = (width < 0);
+ if (flipX) width = - width;
+ bool flipY = (height < 0);
+ if (flipY) height = - height;
+
+ ImageData dest = new ImageData(
+ width, height, depth, palette,
+ scanlinePad, null, 0, null,
+ null, -1, transparentPixel, type,
+ x, y, disposalMethod, delayTime);
+
+ /* Scale the image contents */
+ if (palette.isDirect) blit(BLIT_SRC,
+ this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, 0, 0, 0,
+ ALPHA_OPAQUE, null, 0, 0, 0,
+ dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, 0, 0, 0,
+ flipX, flipY);
+ else blit(BLIT_SRC,
+ this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, null, null, null,
+ ALPHA_OPAQUE, null, 0, 0, 0,
+ dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, null, null, null,
+ flipX, flipY);
+
+ /* Scale the image mask or alpha */
+ if (maskData !is null) {
+ dest.maskPad = this.maskPad;
+ int destBpl = (dest.width + 7) / 8;
+ destBpl = (destBpl + (dest.maskPad - 1)) / dest.maskPad * dest.maskPad;
+ dest.maskData = new byte[destBpl * dest.height];
+ int srcBpl = (this.width + 7) / 8;
+ srcBpl = (srcBpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
+ blit(BLIT_SRC,
+ this.maskData, 1, srcBpl, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+ ALPHA_OPAQUE, null, 0, 0, 0,
+ dest.maskData, 1, destBpl, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+ flipX, flipY);
+ } else if (alpha !is -1) {
+ dest.alpha = this.alpha;
+ } else if (alphaData !is null) {
+ dest.alphaData = new byte[dest.width * dest.height];
+ blit(BLIT_SRC,
+ this.alphaData, 8, this.width, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+ ALPHA_OPAQUE, null, 0, 0, 0,
+ dest.alphaData, 8, dest.width, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+ flipX, flipY);
+ }
+ return dest;
+}
+
+/**
+ * Sets the alpha value at offset x
in
+ * scanline y
in the receiver's alpha data.
+ *
+ * @param x the x coordinate of the alpha value to set
+ * @param y the y coordinate of the alpha value to set
+ * @param alpha the value to set the alpha to
+ *
+ * @exception IllegalArgumentException x
in
+ * scanline y
in the receiver's alpha data to the
+ * values from the array alphas
starting at
+ * startIndex
.
+ *
+ * @param x the x coordinate of the pixel to being setting the alpha values
+ * @param y the y coordinate of the pixel to being setting the alpha values
+ * @param putWidth the width of the alpha values to set
+ * @param alphas the alpha values to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException x
in
+ * scanline y
in the receiver's data.
+ *
+ * @param x the x coordinate of the pixel to set
+ * @param y the y coordinate of the pixel to set
+ * @param pixelValue the value to set the pixel to
+ *
+ * @exception IllegalArgumentException x
in
+ * scanline y
in the receiver's data to the
+ * values from the array pixels
starting at
+ * startIndex
.
+ *
+ * @param x the x position of the pixel to set
+ * @param y the y position of the pixel to set
+ * @param putWidth the width of the pixels to set
+ * @param pixels the pixels to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException x
in
+ * scanline y
in the receiver's data to the
+ * values from the array pixels
starting at
+ * startIndex
.
+ *
+ * @param x the x position of the pixel to set
+ * @param y the y position of the pixel to set
+ * @param putWidth the width of the pixels to set
+ * @param pixels the pixels to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException + * Note: When the source and destination depth, order and masks + * are pairwise equal and the blitter operation is BLIT_SRC, + * the masks are ignored. Hence when not changing the image + * data format, 0 may be specified for the masks. + *
+ * + * @param op the blitter operation: a combination of BLIT_xxx flags + * (see BLIT_xxx constants) + * @param srcData the source byte array containing image data + * @param srcDepth the source depth: one of 8, 16, 24, 32 + * @param srcStride the source number of bytes per line + * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if srcDepth is not 16 or 32 + * @param srcX the top-left x-coord of the source blit region + * @param srcY the top-left y-coord of the source blit region + * @param srcWidth the width of the source blit region + * @param srcHeight the height of the source blit region + * @param srcRedMask the source red channel mask + * @param srcGreenMask the source green channel mask + * @param srcBlueMask the source blue channel mask + * @param alphaMode the alpha blending or mask mode, may be + * an integer 0-255 for global alpha; ignored if BLIT_ALPHA + * not specified in the blitter operations + * (see ALPHA_MODE_xxx constants) + * @param alphaData the alpha blending or mask data, varies depending + * on the value of alphaMode and sometimes ignored + * @param alphaStride the alpha data number of bytes per line + * @param alphaX the top-left x-coord of the alpha blit region + * @param alphaY the top-left y-coord of the alpha blit region + * @param destData the destination byte array containing image data + * @param destDepth the destination depth: one of 8, 16, 24, 32 + * @param destStride the destination number of bytes per line + * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if destDepth is not 16 or 32 + * @param destX the top-left x-coord of the destination blit region + * @param destY the top-left y-coord of the destination blit region + * @param destWidth the width of the destination blit region + * @param destHeight the height of the destination blit region + * @param destRedMask the destination red channel mask + * @param destGreenMask the destination green channel mask + * @param destBlueMask the destination blue channel mask + * @param flipX if true the resulting image is flipped along the vertical axis + * @param flipY if true the resulting image is flipped along the horizontal axis + */ +static void blit(int op, + byte[] srcData, int srcDepth, int srcStride, int srcOrder, + int srcX, int srcY, int srcWidth, int srcHeight, + int srcRedMask, int srcGreenMask, int srcBlueMask, + int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY, + byte[] destData, int destDepth, int destStride, int destOrder, + int destX, int destY, int destWidth, int destHeight, + int destRedMask, int destGreenMask, int destBlueMask, + bool flipX, bool flipY) { + + static_this(); + + if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return; + + // these should be supplied as params later + const int srcAlphaMask = 0, destAlphaMask = 0; + + /*** Prepare scaling data ***/ + int dwm1 = destWidth - 1; + int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0; + int dhm1 = destHeight - 1; + int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0; + + /*** Prepare source-related data ***/ + int sbpp, stype; + switch (srcDepth) { + case 8: + sbpp = 1; + stype = TYPE_GENERIC_8; + break; + case 16: + sbpp = 2; + stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB; + break; + case 24: + sbpp = 3; + stype = TYPE_GENERIC_24; + break; + case 32: + sbpp = 4; + stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int spr = srcY * srcStride + srcX * sbpp; + + /*** Prepare destination-related data ***/ + int dbpp, dtype; + switch (destDepth) { + case 8: + dbpp = 1; + dtype = TYPE_GENERIC_8; + break; + case 16: + dbpp = 2; + dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB; + break; + case 24: + dbpp = 3; + dtype = TYPE_GENERIC_24; + break; + case 32: + dbpp = 4; + dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid destination type"); + return; + } + int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp; + int dprxi = (flipX) ? -dbpp : dbpp; + int dpryi = (flipY) ? -destStride : destStride; + + /*** Prepare special processing data ***/ + int apr; + if ((op & BLIT_ALPHA) !is 0) { + switch (alphaMode) { + case ALPHA_MASK_UNPACKED: + case ALPHA_CHANNEL_SEPARATE: + if (alphaData is null) alphaMode = 0x10000; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_PACKED: + if (alphaData is null) alphaMode = 0x10000; + alphaStride <<= 3; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_INDEX: + //throw new IllegalArgumentException("Invalid alpha type"); + return; + case ALPHA_MASK_RGB: + if (alphaData is null) alphaMode = 0x10000; + apr = 0; + break; + default: + alphaMode = (alphaMode << 16) / 255; // prescale + case ALPHA_CHANNEL_SOURCE: + apr = 0; + break; + } + } else { + alphaMode = 0x10000; + apr = 0; + } + + /*** Blit ***/ + int dp = dpr; + int sp = spr; + if ((alphaMode is 0x10000) && (stype is dtype) && + (srcRedMask is destRedMask) && (srcGreenMask is destGreenMask) && + (srcBlueMask is destBlueMask) && (srcAlphaMask is destAlphaMask)) { + /*** Fast blit (straight copy) ***/ + switch (sbpp) { + case 1: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + destData[dp] = srcData[sp]; + sp += (sfx >>> 16); + } + } + break; + case 2: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + destData[dp] = srcData[sp]; + destData[dp + 1] = srcData[sp + 1]; + sp += (sfx >>> 16) * 2; + } + } + break; + case 3: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + destData[dp] = srcData[sp]; + destData[dp + 1] = srcData[sp + 1]; + destData[dp + 2] = srcData[sp + 2]; + sp += (sfx >>> 16) * 3; + } + } + break; + case 4: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + destData[dp] = srcData[sp]; + destData[dp + 1] = srcData[sp + 1]; + destData[dp + 2] = srcData[sp + 2]; + destData[dp + 3] = srcData[sp + 3]; + sp += (sfx >>> 16) * 4; + } + } + break; + default: + } + return; + } + /*** Comprehensive blit (apply transformations) ***/ + int srcRedShift = getChannelShift(srcRedMask); + byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)]; + int srcGreenShift = getChannelShift(srcGreenMask); + byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)]; + int srcBlueShift = getChannelShift(srcBlueMask); + byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)]; + int srcAlphaShift = getChannelShift(srcAlphaMask); + byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)]; + + int destRedShift = getChannelShift(destRedMask); + int destRedWidth = getChannelWidth(destRedMask, destRedShift); + byte[] destReds = ANY_TO_EIGHT[destRedWidth]; + int destRedPreShift = 8 - destRedWidth; + int destGreenShift = getChannelShift(destGreenMask); + int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift); + byte[] destGreens = ANY_TO_EIGHT[destGreenWidth]; + int destGreenPreShift = 8 - destGreenWidth; + int destBlueShift = getChannelShift(destBlueMask); + int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift); + byte[] destBlues = ANY_TO_EIGHT[destBlueWidth]; + int destBluePreShift = 8 - destBlueWidth; + int destAlphaShift = getChannelShift(destAlphaMask); + int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift); + byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth]; + int destAlphaPreShift = 8 - destAlphaWidth; + + int ap = apr, alpha = alphaMode; + int r = 0, g = 0, b = 0, a = 0; + int rq = 0, gq = 0, bq = 0, aq = 0; + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, + sp = spr += (sfy >>> 16) * srcStride, + ap = apr += (sfy >>> 16) * alphaStride, + sfy = (sfy & 0xffff) + sfyi, + dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, + dp += dprxi, + sfx = (sfx & 0xffff) + sfxi) { + /*** READ NEXT PIXEL ***/ + switch (stype) { + case TYPE_GENERIC_8: { + int data = srcData[sp] & 0xff; + sp += (sfx >>> 16); + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_MSB: { + int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff); + sp += (sfx >>> 16) * 2; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_LSB: { + int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff); + sp += (sfx >>> 16) * 2; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_24: { + int data = (( ((srcData[sp] & 0xff) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp + 2] & 0xff); + sp += (sfx >>> 16) * 3; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_MSB: { + int data = (( (( ((srcData[sp] & 0xff) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp + 2] & 0xff)) << 8) | + (srcData[sp + 3] & 0xff); + sp += (sfx >>> 16) * 4; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_LSB: { + int data = (( (( ((srcData[sp + 3] & 0xff) << 8) | + (srcData[sp + 2] & 0xff)) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp] & 0xff); + sp += (sfx >>> 16) * 4; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + default: + } + + /*** DO SPECIAL PROCESSING IF REQUIRED ***/ + switch (alphaMode) { + case ALPHA_CHANNEL_SEPARATE: + alpha = ((alphaData[ap] & 0xff) << 16) / 255; + ap += (sfx >> 16); + break; + case ALPHA_CHANNEL_SOURCE: + alpha = (a << 16) / 255; + break; + case ALPHA_MASK_UNPACKED: + alpha = (alphaData[ap] !is 0) ? 0x10000 : 0; + ap += (sfx >> 16); + break; + case ALPHA_MASK_PACKED: + alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000; + ap += (sfx >> 16); + break; + case ALPHA_MASK_RGB: + alpha = 0x10000; + for (int i = 0; i < alphaData.length; i += 3) { + if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) { + alpha = 0x0000; + break; + } + } + break; + default: + } + if (alpha !is 0x10000) { + if (alpha is 0x0000) continue; + switch (dtype) { + case TYPE_GENERIC_8: { + int data = destData[dp] & 0xff; + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_MSB: { + int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_LSB: { + int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_24: { + int data = (( ((destData[dp] & 0xff) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp + 2] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_MSB: { + int data = (( (( ((destData[dp] & 0xff) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp + 2] & 0xff)) << 8) | + (destData[dp + 3] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_LSB: { + int data = (( (( ((destData[dp + 3] & 0xff) << 8) | + (destData[dp + 2] & 0xff)) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + default: + } + // Perform alpha blending + a = aq + ((a - aq) * alpha >> 16); + r = rq + ((r - rq) * alpha >> 16); + g = gq + ((g - gq) * alpha >> 16); + b = bq + ((b - bq) * alpha >> 16); + } + + /*** WRITE NEXT PIXEL ***/ + int data = + (r >>> destRedPreShift << destRedShift) | + (g >>> destGreenPreShift << destGreenShift) | + (b >>> destBluePreShift << destBlueShift) | + (a >>> destAlphaPreShift << destAlphaShift); + switch (dtype) { + case TYPE_GENERIC_8: { + destData[dp] = cast(byte) data; + } break; + case TYPE_GENERIC_16_MSB: { + destData[dp] = cast(byte) (data >>> 8); + destData[dp + 1] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_16_LSB: { + destData[dp] = cast(byte) (data & 0xff); + destData[dp + 1] = cast(byte) (data >>> 8); + } break; + case TYPE_GENERIC_24: { + destData[dp] = cast(byte) (data >>> 16); + destData[dp + 1] = cast(byte) (data >>> 8); + destData[dp + 2] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_32_MSB: { + destData[dp] = cast(byte) (data >>> 24); + destData[dp + 1] = cast(byte) (data >>> 16); + destData[dp + 2] = cast(byte) (data >>> 8); + destData[dp + 3] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_32_LSB: { + destData[dp] = cast(byte) (data & 0xff); + destData[dp + 1] = cast(byte) (data >>> 8); + destData[dp + 2] = cast(byte) (data >>> 16); + destData[dp + 3] = cast(byte) (data >>> 24); + } break; + default: + } + } + } +} + +/** + * Blits an index palette image into an index palette image. + *+ * Note: The source and destination red, green, and blue + * arrays may be null if no alpha blending or dither is to be + * performed. + *
+ * + * @param op the blitter operation: a combination of BLIT_xxx flags + * (see BLIT_xxx constants) + * @param srcData the source byte array containing image data + * @param srcDepth the source depth: one of 1, 2, 4, 8 + * @param srcStride the source number of bytes per line + * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if srcDepth is not 1 + * @param srcX the top-left x-coord of the source blit region + * @param srcY the top-left y-coord of the source blit region + * @param srcWidth the width of the source blit region + * @param srcHeight the height of the source blit region + * @param srcReds the source palette red component intensities + * @param srcGreens the source palette green component intensities + * @param srcBlues the source palette blue component intensities + * @param alphaMode the alpha blending or mask mode, may be + * an integer 0-255 for global alpha; ignored if BLIT_ALPHA + * not specified in the blitter operations + * (see ALPHA_MODE_xxx constants) + * @param alphaData the alpha blending or mask data, varies depending + * on the value of alphaMode and sometimes ignored + * @param alphaStride the alpha data number of bytes per line + * @param alphaX the top-left x-coord of the alpha blit region + * @param alphaY the top-left y-coord of the alpha blit region + * @param destData the destination byte array containing image data + * @param destDepth the destination depth: one of 1, 2, 4, 8 + * @param destStride the destination number of bytes per line + * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if destDepth is not 1 + * @param destX the top-left x-coord of the destination blit region + * @param destY the top-left y-coord of the destination blit region + * @param destWidth the width of the destination blit region + * @param destHeight the height of the destination blit region + * @param destReds the destination palette red component intensities + * @param destGreens the destination palette green component intensities + * @param destBlues the destination palette blue component intensities + * @param flipX if true the resulting image is flipped along the vertical axis + * @param flipY if true the resulting image is flipped along the horizontal axis + */ +static void blit(int op, + byte[] srcData, int srcDepth, int srcStride, int srcOrder, + int srcX, int srcY, int srcWidth, int srcHeight, + byte[] srcReds, byte[] srcGreens, byte[] srcBlues, + int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY, + byte[] destData, int destDepth, int destStride, int destOrder, + int destX, int destY, int destWidth, int destHeight, + byte[] destReds, byte[] destGreens, byte[] destBlues, + bool flipX, bool flipY) { + + static_this(); + + if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return; + + /*** Prepare scaling data ***/ + int dwm1 = destWidth - 1; + int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0; + int dhm1 = destHeight - 1; + int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0; + + /*** Prepare source-related data ***/ + int stype; + switch (srcDepth) { + case 8: + stype = TYPE_INDEX_8; + break; + case 4: + srcStride <<= 1; + stype = TYPE_INDEX_4; + break; + case 2: + srcStride <<= 2; + stype = TYPE_INDEX_2; + break; + case 1: + srcStride <<= 3; + stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int spr = srcY * srcStride + srcX; + + /*** Prepare destination-related data ***/ + int dtype; + switch (destDepth) { + case 8: + dtype = TYPE_INDEX_8; + break; + case 4: + destStride <<= 1; + dtype = TYPE_INDEX_4; + break; + case 2: + destStride <<= 2; + dtype = TYPE_INDEX_2; + break; + case 1: + destStride <<= 3; + dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX); + int dprxi = (flipX) ? -1 : 1; + int dpryi = (flipY) ? -destStride : destStride; + + /*** Prepare special processing data ***/ + int apr; + if ((op & BLIT_ALPHA) !is 0) { + switch (alphaMode) { + case ALPHA_MASK_UNPACKED: + case ALPHA_CHANNEL_SEPARATE: + if (alphaData is null) alphaMode = 0x10000; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_PACKED: + if (alphaData is null) alphaMode = 0x10000; + alphaStride <<= 3; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_INDEX: + case ALPHA_MASK_RGB: + if (alphaData is null) alphaMode = 0x10000; + apr = 0; + break; + default: + alphaMode = (alphaMode << 16) / 255; // prescale + case ALPHA_CHANNEL_SOURCE: + apr = 0; + break; + } + } else { + alphaMode = 0x10000; + apr = 0; + } + bool ditherEnabled = (op & BLIT_DITHER) !is 0; + + /*** Blit ***/ + int dp = dpr; + int sp = spr; + int ap = apr; + int destPaletteSize = 1 << destDepth; + if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length; + byte[] paletteMapping = null; + bool isExactPaletteMapping = true; + switch (alphaMode) { + case 0x10000: + /*** If the palettes and formats are equivalent use a one-to-one mapping ***/ + if ((stype is dtype) && + (srcReds is destReds) && (srcGreens is destGreens) && (srcBlues is destBlues)) { + paletteMapping = ONE_TO_ONE_MAPPING; + break; + /*** If palettes have not been supplied, supply a suitable mapping ***/ + } else if ((srcReds is null) || (destReds is null)) { + if (srcDepth <= destDepth) { + paletteMapping = ONE_TO_ONE_MAPPING; + } else { + paletteMapping = new byte[1 << srcDepth]; + int mask = (0xff << destDepth) >>> 8; + for (int i = 0; i < paletteMapping.length; ++i) paletteMapping[i] = cast(byte)(i & mask); + } + break; + } + case ALPHA_MASK_UNPACKED: + case ALPHA_MASK_PACKED: + case ALPHA_MASK_INDEX: + case ALPHA_MASK_RGB: + /*** Generate a palette mapping ***/ + int srcPaletteSize = 1 << srcDepth; + paletteMapping = new byte[srcPaletteSize]; + if ((srcReds !is null) && (srcReds.length < srcPaletteSize)) srcPaletteSize = srcReds.length; + for (int i = 0, r, g, b, index; i < srcPaletteSize; ++i) { + r = srcReds[i] & 0xff; + g = srcGreens[i] & 0xff; + b = srcBlues[i] & 0xff; + index = 0; + int minDistance = 0x7fffffff; + for (int j = 0, dr, dg, db, distance; j < destPaletteSize; ++j) { + dr = (destReds[j] & 0xff) - r; + dg = (destGreens[j] & 0xff) - g; + db = (destBlues[j] & 0xff) - b; + distance = dr * dr + dg * dg + db * db; + if (distance < minDistance) { + index = j; + if (distance is 0) break; + minDistance = distance; + } + } + paletteMapping[i] = cast(byte)index; + if (minDistance !is 0) isExactPaletteMapping = false; + } + break; + default: + } + if ((paletteMapping !is null) && (isExactPaletteMapping || ! ditherEnabled)) { + if ((stype is dtype) && (alphaMode is 0x10000)) { + /*** Fast blit (copy w/ mapping) ***/ + switch (stype) { + case TYPE_INDEX_8: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + destData[dp] = paletteMapping[srcData[sp] & 0xff]; + sp += (sfx >>> 16); + } + } + break; + case TYPE_INDEX_4: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + int v; + if ((sp & 1) !is 0) v = paletteMapping[srcData[sp >> 1] & 0x0f]; + else v = (srcData[sp >> 1] >>> 4) & 0x0f; + sp += (sfx >>> 16); + if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | v); + else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (v << 4)); + } + } + break; + case TYPE_INDEX_2: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + int index = paletteMapping[(srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03]; + sp += (sfx >>> 16); + int shift = 6 - (dp & 3) * 2; + destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift)); + } + } + break; + case TYPE_INDEX_1_MSB: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + int index = paletteMapping[(srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01]; + sp += (sfx >>> 16); + int shift = 7 - (dp & 7); + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift)); + } + } + break; + case TYPE_INDEX_1_LSB: + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) { + int index = paletteMapping[(srcData[sp >> 3] >>> (sp & 7)) & 0x01]; + sp += (sfx >>> 16); + int shift = dp & 7; + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift)); + } + } + break; + default: + } + } else { + /*** Convert between indexed modes using mapping and mask ***/ + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, + sp = spr += (sfy >>> 16) * srcStride, + sfy = (sfy & 0xffff) + sfyi, + dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, + dp += dprxi, + sfx = (sfx & 0xffff) + sfxi) { + int index; + /*** READ NEXT PIXEL ***/ + switch (stype) { + case TYPE_INDEX_8: + index = srcData[sp] & 0xff; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_4: + if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f; + else index = (srcData[sp >> 1] >>> 4) & 0x0f; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_2: + index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_MSB: + index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_LSB: + index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01; + sp += (sfx >>> 16); + break; + default: + return; + } + /*** APPLY MASK ***/ + switch (alphaMode) { + case ALPHA_MASK_UNPACKED: { + byte mask = alphaData[ap]; + ap += (sfx >> 16); + if (mask is 0) continue; + } break; + case ALPHA_MASK_PACKED: { + int mask = alphaData[ap >> 3] & (1 << (ap & 7)); + ap += (sfx >> 16); + if (mask is 0) continue; + } break; + case ALPHA_MASK_INDEX: { + int i = 0; + while (i < alphaData.length) { + if (index is (alphaData[i] & 0xff)) break; + } + if (i < alphaData.length) continue; + } break; + case ALPHA_MASK_RGB: { + byte r = srcReds[index], g = srcGreens[index], b = srcBlues[index]; + int i = 0; + while (i < alphaData.length) { + if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) break; + i += 3; + } + if (i < alphaData.length) continue; + } break; + default: + } + index = paletteMapping[index] & 0xff; + + /*** WRITE NEXT PIXEL ***/ + switch (dtype) { + case TYPE_INDEX_8: + destData[dp] = cast(byte) index; + break; + case TYPE_INDEX_4: + if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | index); + else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (index << 4)); + break; + case TYPE_INDEX_2: { + int shift = 6 - (dp & 3) * 2; + destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift)); + } break; + case TYPE_INDEX_1_MSB: { + int shift = 7 - (dp & 7); + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift)); + } break; + case TYPE_INDEX_1_LSB: { + int shift = dp & 7; + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift)); + } break; + default: + } + } + } + } + return; + } + + /*** Comprehensive blit (apply transformations) ***/ + int alpha = alphaMode; + int index = 0; + int indexq = 0; + int lastindex = 0, lastr = -1, lastg = -1, lastb = -1; + int[] rerr, gerr, berr; + if (ditherEnabled) { + rerr = new int[destWidth + 2]; + gerr = new int[destWidth + 2]; + berr = new int[destWidth + 2]; + } else { + rerr = null; gerr = null; berr = null; + } + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, + sp = spr += (sfy >>> 16) * srcStride, + ap = apr += (sfy >>> 16) * alphaStride, + sfy = (sfy & 0xffff) + sfyi, + dp = dpr += dpryi) { + int lrerr = 0, lgerr = 0, lberr = 0; + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, + dp += dprxi, + sfx = (sfx & 0xffff) + sfxi) { + /*** READ NEXT PIXEL ***/ + switch (stype) { + case TYPE_INDEX_8: + index = srcData[sp] & 0xff; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_4: + if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f; + else index = (srcData[sp >> 1] >>> 4) & 0x0f; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_2: + index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_MSB: + index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_LSB: + index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01; + sp += (sfx >>> 16); + break; + default: + } + + /*** DO SPECIAL PROCESSING IF REQUIRED ***/ + int r = srcReds[index] & 0xff, g = srcGreens[index] & 0xff, b = srcBlues[index] & 0xff; + switch (alphaMode) { + case ALPHA_CHANNEL_SEPARATE: + alpha = ((alphaData[ap] & 0xff) << 16) / 255; + ap += (sfx >> 16); + break; + case ALPHA_MASK_UNPACKED: + alpha = (alphaData[ap] !is 0) ? 0x10000 : 0; + ap += (sfx >> 16); + break; + case ALPHA_MASK_PACKED: + alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000; + ap += (sfx >> 16); + break; + case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices + int i = 0; + while (i < alphaData.length) { + if (index is (alphaData[i] & 0xff)) break; + } + if (i < alphaData.length) continue; + } break; + case ALPHA_MASK_RGB: { + int i = 0; + while (i < alphaData.length) { + if ((r is (alphaData[i] & 0xff)) && + (g is (alphaData[i + 1] & 0xff)) && + (b is (alphaData[i + 2] & 0xff))) break; + i += 3; + } + if (i < alphaData.length) continue; + } break; + default: + } + if (alpha !is 0x10000) { + if (alpha is 0x0000) continue; + switch (dtype) { + case TYPE_INDEX_8: + indexq = destData[dp] & 0xff; + break; + case TYPE_INDEX_4: + if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f; + else indexq = (destData[dp >> 1] >>> 4) & 0x0f; + break; + case TYPE_INDEX_2: + indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03; + break; + case TYPE_INDEX_1_MSB: + indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01; + break; + case TYPE_INDEX_1_LSB: + indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01; + break; + default: + } + // Perform alpha blending + int rq = destReds[indexq] & 0xff; + int gq = destGreens[indexq] & 0xff; + int bq = destBlues[indexq] & 0xff; + r = rq + ((r - rq) * alpha >> 16); + g = gq + ((g - gq) * alpha >> 16); + b = bq + ((b - bq) * alpha >> 16); + } + + /*** MAP COLOR TO THE PALETTE ***/ + if (ditherEnabled) { + // Floyd-Steinberg error diffusion + r += rerr[dx] >> 4; + if (r < 0) r = 0; else if (r > 255) r = 255; + g += gerr[dx] >> 4; + if (g < 0) g = 0; else if (g > 255) g = 255; + b += berr[dx] >> 4; + if (b < 0) b = 0; else if (b > 255) b = 255; + rerr[dx] = lrerr; + gerr[dx] = lgerr; + berr[dx] = lberr; + } + if (r !is lastr || g !is lastg || b !is lastb) { + // moving the variable declarations out seems to make the JDK JIT happier... + for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) { + dr = (destReds[j] & 0xff) - r; + dg = (destGreens[j] & 0xff) - g; + db = (destBlues[j] & 0xff) - b; + distance = dr * dr + dg * dg + db * db; + if (distance < minDistance) { + lastindex = j; + if (distance is 0) break; + minDistance = distance; + } + } + lastr = r; lastg = g; lastb = b; + } + if (ditherEnabled) { + // Floyd-Steinberg error diffusion, cont'd... + int dxm1 = dx - 1, dxp1 = dx + 1; + int acc; + rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr; + rerr[dx] += acc += lrerr + lrerr; + rerr[dxm1] += acc + lrerr + lrerr; + gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr; + gerr[dx] += acc += lgerr + lgerr; + gerr[dxm1] += acc + lgerr + lgerr; + berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr; + berr[dx] += acc += lberr + lberr; + berr[dxm1] += acc + lberr + lberr; + } + + /*** WRITE NEXT PIXEL ***/ + switch (dtype) { + case TYPE_INDEX_8: + destData[dp] = cast(byte) lastindex; + break; + case TYPE_INDEX_4: + if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | lastindex); + else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4)); + break; + case TYPE_INDEX_2: { + int shift = 6 - (dp & 3) * 2; + destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift)); + } break; + case TYPE_INDEX_1_MSB: { + int shift = 7 - (dp & 7); + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift)); + } break; + case TYPE_INDEX_1_LSB: { + int shift = dp & 7; + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift)); + } break; + default: + } + } + } +} + +/** + * Blits an index palette image into a direct palette image. + *+ * Note: The source and destination masks and palettes must + * always be fully specified. + *
+ * + * @param op the blitter operation: a combination of BLIT_xxx flags + * (see BLIT_xxx constants) + * @param srcData the source byte array containing image data + * @param srcDepth the source depth: one of 1, 2, 4, 8 + * @param srcStride the source number of bytes per line + * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if srcDepth is not 1 + * @param srcX the top-left x-coord of the source blit region + * @param srcY the top-left y-coord of the source blit region + * @param srcWidth the width of the source blit region + * @param srcHeight the height of the source blit region + * @param srcReds the source palette red component intensities + * @param srcGreens the source palette green component intensities + * @param srcBlues the source palette blue component intensities + * @param alphaMode the alpha blending or mask mode, may be + * an integer 0-255 for global alpha; ignored if BLIT_ALPHA + * not specified in the blitter operations + * (see ALPHA_MODE_xxx constants) + * @param alphaData the alpha blending or mask data, varies depending + * on the value of alphaMode and sometimes ignored + * @param alphaStride the alpha data number of bytes per line + * @param alphaX the top-left x-coord of the alpha blit region + * @param alphaY the top-left y-coord of the alpha blit region + * @param destData the destination byte array containing image data + * @param destDepth the destination depth: one of 8, 16, 24, 32 + * @param destStride the destination number of bytes per line + * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if destDepth is not 16 or 32 + * @param destX the top-left x-coord of the destination blit region + * @param destY the top-left y-coord of the destination blit region + * @param destWidth the width of the destination blit region + * @param destHeight the height of the destination blit region + * @param destRedMask the destination red channel mask + * @param destGreenMask the destination green channel mask + * @param destBlueMask the destination blue channel mask + * @param flipX if true the resulting image is flipped along the vertical axis + * @param flipY if true the resulting image is flipped along the horizontal axis + */ +static void blit(int op, + byte[] srcData, int srcDepth, int srcStride, int srcOrder, + int srcX, int srcY, int srcWidth, int srcHeight, + byte[] srcReds, byte[] srcGreens, byte[] srcBlues, + int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY, + byte[] destData, int destDepth, int destStride, int destOrder, + int destX, int destY, int destWidth, int destHeight, + int destRedMask, int destGreenMask, int destBlueMask, + bool flipX, bool flipY) { + + static_this(); + + if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return; + + // these should be supplied as params later + int destAlphaMask = 0; + + /*** Prepare scaling data ***/ + int dwm1 = destWidth - 1; + int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0; + int dhm1 = destHeight - 1; + int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0; + + /*** Prepare source-related data ***/ + int stype; + switch (srcDepth) { + case 8: + stype = TYPE_INDEX_8; + break; + case 4: + srcStride <<= 1; + stype = TYPE_INDEX_4; + break; + case 2: + srcStride <<= 2; + stype = TYPE_INDEX_2; + break; + case 1: + srcStride <<= 3; + stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int spr = srcY * srcStride + srcX; + + /*** Prepare destination-related data ***/ + int dbpp, dtype; + switch (destDepth) { + case 8: + dbpp = 1; + dtype = TYPE_GENERIC_8; + break; + case 16: + dbpp = 2; + dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB; + break; + case 24: + dbpp = 3; + dtype = TYPE_GENERIC_24; + break; + case 32: + dbpp = 4; + dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid destination type"); + return; + } + int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp; + int dprxi = (flipX) ? -dbpp : dbpp; + int dpryi = (flipY) ? -destStride : destStride; + + /*** Prepare special processing data ***/ + int apr; + if ((op & BLIT_ALPHA) !is 0) { + switch (alphaMode) { + case ALPHA_MASK_UNPACKED: + case ALPHA_CHANNEL_SEPARATE: + if (alphaData is null) alphaMode = 0x10000; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_PACKED: + if (alphaData is null) alphaMode = 0x10000; + alphaStride <<= 3; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_INDEX: + case ALPHA_MASK_RGB: + if (alphaData is null) alphaMode = 0x10000; + apr = 0; + break; + default: + alphaMode = (alphaMode << 16) / 255; // prescale + case ALPHA_CHANNEL_SOURCE: + apr = 0; + break; + } + } else { + alphaMode = 0x10000; + apr = 0; + } + + /*** Comprehensive blit (apply transformations) ***/ + int destRedShift = getChannelShift(destRedMask); + int destRedWidth = getChannelWidth(destRedMask, destRedShift); + byte[] destReds = ANY_TO_EIGHT[destRedWidth]; + int destRedPreShift = 8 - destRedWidth; + int destGreenShift = getChannelShift(destGreenMask); + int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift); + byte[] destGreens = ANY_TO_EIGHT[destGreenWidth]; + int destGreenPreShift = 8 - destGreenWidth; + int destBlueShift = getChannelShift(destBlueMask); + int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift); + byte[] destBlues = ANY_TO_EIGHT[destBlueWidth]; + int destBluePreShift = 8 - destBlueWidth; + int destAlphaShift = getChannelShift(destAlphaMask); + int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift); + byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth]; + int destAlphaPreShift = 8 - destAlphaWidth; + + int dp = dpr; + int sp = spr; + int ap = apr, alpha = alphaMode; + int r = 0, g = 0, b = 0, a = 0, index = 0; + int rq = 0, gq = 0, bq = 0, aq = 0; + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, + sp = spr += (sfy >>> 16) * srcStride, + ap = apr += (sfy >>> 16) * alphaStride, + sfy = (sfy & 0xffff) + sfyi, + dp = dpr += dpryi) { + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, + dp += dprxi, + sfx = (sfx & 0xffff) + sfxi) { + /*** READ NEXT PIXEL ***/ + switch (stype) { + case TYPE_INDEX_8: + index = srcData[sp] & 0xff; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_4: + if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f; + else index = (srcData[sp >> 1] >>> 4) & 0x0f; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_2: + index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_MSB: + index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01; + sp += (sfx >>> 16); + break; + case TYPE_INDEX_1_LSB: + index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01; + sp += (sfx >>> 16); + break; + default: + } + + /*** DO SPECIAL PROCESSING IF REQUIRED ***/ + r = srcReds[index] & 0xff; + g = srcGreens[index] & 0xff; + b = srcBlues[index] & 0xff; + switch (alphaMode) { + case ALPHA_CHANNEL_SEPARATE: + alpha = ((alphaData[ap] & 0xff) << 16) / 255; + ap += (sfx >> 16); + break; + case ALPHA_MASK_UNPACKED: + alpha = (alphaData[ap] !is 0) ? 0x10000 : 0; + ap += (sfx >> 16); + break; + case ALPHA_MASK_PACKED: + alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000; + ap += (sfx >> 16); + break; + case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices + int i = 0; + while (i < alphaData.length) { + if (index is (alphaData[i] & 0xff)) break; + } + if (i < alphaData.length) continue; + } break; + case ALPHA_MASK_RGB: { + int i = 0; + while (i < alphaData.length) { + if ((r is (alphaData[i] & 0xff)) && + (g is (alphaData[i + 1] & 0xff)) && + (b is (alphaData[i + 2] & 0xff))) break; + i += 3; + } + if (i < alphaData.length) continue; + } break; + default: + } + if (alpha !is 0x10000) { + if (alpha is 0x0000) continue; + switch (dtype) { + case TYPE_GENERIC_8: { + int data = destData[dp] & 0xff; + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_MSB: { + int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_LSB: { + int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_24: { + int data = (( ((destData[dp] & 0xff) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp + 2] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_MSB: { + int data = (( (( ((destData[dp] & 0xff) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp + 2] & 0xff)) << 8) | + (destData[dp + 3] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_LSB: { + int data = (( (( ((destData[dp + 3] & 0xff) << 8) | + (destData[dp + 2] & 0xff)) << 8) | + (destData[dp + 1] & 0xff)) << 8) | + (destData[dp] & 0xff); + rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff; + gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff; + bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff; + aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff; + } break; + default: + } + // Perform alpha blending + a = aq + ((a - aq) * alpha >> 16); + r = rq + ((r - rq) * alpha >> 16); + g = gq + ((g - gq) * alpha >> 16); + b = bq + ((b - bq) * alpha >> 16); + } + + /*** WRITE NEXT PIXEL ***/ + int data = + (r >>> destRedPreShift << destRedShift) | + (g >>> destGreenPreShift << destGreenShift) | + (b >>> destBluePreShift << destBlueShift) | + (a >>> destAlphaPreShift << destAlphaShift); + switch (dtype) { + case TYPE_GENERIC_8: { + destData[dp] = cast(byte) data; + } break; + case TYPE_GENERIC_16_MSB: { + destData[dp] = cast(byte) (data >>> 8); + destData[dp + 1] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_16_LSB: { + destData[dp] = cast(byte) (data & 0xff); + destData[dp + 1] = cast(byte) (data >>> 8); + } break; + case TYPE_GENERIC_24: { + destData[dp] = cast(byte) (data >>> 16); + destData[dp + 1] = cast(byte) (data >>> 8); + destData[dp + 2] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_32_MSB: { + destData[dp] = cast(byte) (data >>> 24); + destData[dp + 1] = cast(byte) (data >>> 16); + destData[dp + 2] = cast(byte) (data >>> 8); + destData[dp + 3] = cast(byte) (data & 0xff); + } break; + case TYPE_GENERIC_32_LSB: { + destData[dp] = cast(byte) (data & 0xff); + destData[dp + 1] = cast(byte) (data >>> 8); + destData[dp + 2] = cast(byte) (data >>> 16); + destData[dp + 3] = cast(byte) (data >>> 24); + } break; + default: + } + } + } +} + +/** + * Blits a direct palette image into an index palette image. + *+ * Note: The source and destination masks and palettes must + * always be fully specified. + *
+ * + * @param op the blitter operation: a combination of BLIT_xxx flags + * (see BLIT_xxx constants) + * @param srcData the source byte array containing image data + * @param srcDepth the source depth: one of 8, 16, 24, 32 + * @param srcStride the source number of bytes per line + * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if srcDepth is not 16 or 32 + * @param srcX the top-left x-coord of the source blit region + * @param srcY the top-left y-coord of the source blit region + * @param srcWidth the width of the source blit region + * @param srcHeight the height of the source blit region + * @param srcRedMask the source red channel mask + * @param srcGreenMask the source green channel mask + * @param srcBlueMask the source blue channel mask + * @param alphaMode the alpha blending or mask mode, may be + * an integer 0-255 for global alpha; ignored if BLIT_ALPHA + * not specified in the blitter operations + * (see ALPHA_MODE_xxx constants) + * @param alphaData the alpha blending or mask data, varies depending + * on the value of alphaMode and sometimes ignored + * @param alphaStride the alpha data number of bytes per line + * @param alphaX the top-left x-coord of the alpha blit region + * @param alphaY the top-left y-coord of the alpha blit region + * @param destData the destination byte array containing image data + * @param destDepth the destination depth: one of 1, 2, 4, 8 + * @param destStride the destination number of bytes per line + * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST; + * ignored if destDepth is not 1 + * @param destX the top-left x-coord of the destination blit region + * @param destY the top-left y-coord of the destination blit region + * @param destWidth the width of the destination blit region + * @param destHeight the height of the destination blit region + * @param destReds the destination palette red component intensities + * @param destGreens the destination palette green component intensities + * @param destBlues the destination palette blue component intensities + * @param flipX if true the resulting image is flipped along the vertical axis + * @param flipY if true the resulting image is flipped along the horizontal axis + */ +static void blit(int op, + byte[] srcData, int srcDepth, int srcStride, int srcOrder, + int srcX, int srcY, int srcWidth, int srcHeight, + int srcRedMask, int srcGreenMask, int srcBlueMask, + int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY, + byte[] destData, int destDepth, int destStride, int destOrder, + int destX, int destY, int destWidth, int destHeight, + byte[] destReds, byte[] destGreens, byte[] destBlues, + bool flipX, bool flipY) { + + static_this(); + + if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return; + + // these should be supplied as params later + int srcAlphaMask = 0; + + /*** Prepare scaling data ***/ + int dwm1 = destWidth - 1; + int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0; + int dhm1 = destHeight - 1; + int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0; + + /*** Prepare source-related data ***/ + int sbpp, stype; + switch (srcDepth) { + case 8: + sbpp = 1; + stype = TYPE_GENERIC_8; + break; + case 16: + sbpp = 2; + stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB; + break; + case 24: + sbpp = 3; + stype = TYPE_GENERIC_24; + break; + case 32: + sbpp = 4; + stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int spr = srcY * srcStride + srcX * sbpp; + + /*** Prepare destination-related data ***/ + int dtype; + switch (destDepth) { + case 8: + dtype = TYPE_INDEX_8; + break; + case 4: + destStride <<= 1; + dtype = TYPE_INDEX_4; + break; + case 2: + destStride <<= 2; + dtype = TYPE_INDEX_2; + break; + case 1: + destStride <<= 3; + dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB; + break; + default: + //throw new IllegalArgumentException("Invalid source type"); + return; + } + int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX); + int dprxi = (flipX) ? -1 : 1; + int dpryi = (flipY) ? -destStride : destStride; + + /*** Prepare special processing data ***/ + int apr; + if ((op & BLIT_ALPHA) !is 0) { + switch (alphaMode) { + case ALPHA_MASK_UNPACKED: + case ALPHA_CHANNEL_SEPARATE: + if (alphaData is null) alphaMode = 0x10000; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_PACKED: + if (alphaData is null) alphaMode = 0x10000; + alphaStride <<= 3; + apr = alphaY * alphaStride + alphaX; + break; + case ALPHA_MASK_INDEX: + //throw new IllegalArgumentException("Invalid alpha type"); + return; + case ALPHA_MASK_RGB: + if (alphaData is null) alphaMode = 0x10000; + apr = 0; + break; + default: + alphaMode = (alphaMode << 16) / 255; // prescale + case ALPHA_CHANNEL_SOURCE: + apr = 0; + break; + } + } else { + alphaMode = 0x10000; + apr = 0; + } + bool ditherEnabled = (op & BLIT_DITHER) !is 0; + + /*** Comprehensive blit (apply transformations) ***/ + int srcRedShift = getChannelShift(srcRedMask); + byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)]; + int srcGreenShift = getChannelShift(srcGreenMask); + byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)]; + int srcBlueShift = getChannelShift(srcBlueMask); + byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)]; + int srcAlphaShift = getChannelShift(srcAlphaMask); + byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)]; + + int dp = dpr; + int sp = spr; + int ap = apr, alpha = alphaMode; + int r = 0, g = 0, b = 0, a = 0; + int indexq = 0; + int lastindex = 0, lastr = -1, lastg = -1, lastb = -1; + int[] rerr, gerr, berr; + int destPaletteSize = 1 << destDepth; + if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length; + if (ditherEnabled) { + rerr = new int[destWidth + 2]; + gerr = new int[destWidth + 2]; + berr = new int[destWidth + 2]; + } else { + rerr = null; gerr = null; berr = null; + } + for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, + sp = spr += (sfy >>> 16) * srcStride, + ap = apr += (sfy >>> 16) * alphaStride, + sfy = (sfy & 0xffff) + sfyi, + dp = dpr += dpryi) { + int lrerr = 0, lgerr = 0, lberr = 0; + for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, + dp += dprxi, + sfx = (sfx & 0xffff) + sfxi) { + /*** READ NEXT PIXEL ***/ + switch (stype) { + case TYPE_GENERIC_8: { + int data = srcData[sp] & 0xff; + sp += (sfx >>> 16); + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_MSB: { + int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff); + sp += (sfx >>> 16) * 2; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_16_LSB: { + int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff); + sp += (sfx >>> 16) * 2; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_24: { + int data = (( ((srcData[sp] & 0xff) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp + 2] & 0xff); + sp += (sfx >>> 16) * 3; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_MSB: { + int data = (( (( ((srcData[sp] & 0xff) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp + 2] & 0xff)) << 8) | + (srcData[sp + 3] & 0xff); + sp += (sfx >>> 16) * 4; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + case TYPE_GENERIC_32_LSB: { + int data = (( (( ((srcData[sp + 3] & 0xff) << 8) | + (srcData[sp + 2] & 0xff)) << 8) | + (srcData[sp + 1] & 0xff)) << 8) | + (srcData[sp] & 0xff); + sp += (sfx >>> 16) * 4; + r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff; + g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff; + b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff; + a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff; + } break; + default: + } + + /*** DO SPECIAL PROCESSING IF REQUIRED ***/ + switch (alphaMode) { + case ALPHA_CHANNEL_SEPARATE: + alpha = ((alphaData[ap] & 0xff) << 16) / 255; + ap += (sfx >> 16); + break; + case ALPHA_CHANNEL_SOURCE: + alpha = (a << 16) / 255; + break; + case ALPHA_MASK_UNPACKED: + alpha = (alphaData[ap] !is 0) ? 0x10000 : 0; + ap += (sfx >> 16); + break; + case ALPHA_MASK_PACKED: + alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000; + ap += (sfx >> 16); + break; + case ALPHA_MASK_RGB: + alpha = 0x10000; + for (int i = 0; i < alphaData.length; i += 3) { + if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) { + alpha = 0x0000; + break; + } + } + break; + default: + } + if (alpha !is 0x10000) { + if (alpha is 0x0000) continue; + switch (dtype) { + case TYPE_INDEX_8: + indexq = destData[dp] & 0xff; + break; + case TYPE_INDEX_4: + if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f; + else indexq = (destData[dp >> 1] >>> 4) & 0x0f; + break; + case TYPE_INDEX_2: + indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03; + break; + case TYPE_INDEX_1_MSB: + indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01; + break; + case TYPE_INDEX_1_LSB: + indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01; + break; + default: + } + // Perform alpha blending + int rq = destReds[indexq] & 0xff; + int gq = destGreens[indexq] & 0xff; + int bq = destBlues[indexq] & 0xff; + r = rq + ((r - rq) * alpha >> 16); + g = gq + ((g - gq) * alpha >> 16); + b = bq + ((b - bq) * alpha >> 16); + } + + /*** MAP COLOR TO THE PALETTE ***/ + if (ditherEnabled) { + // Floyd-Steinberg error diffusion + r += rerr[dx] >> 4; + if (r < 0) r = 0; else if (r > 255) r = 255; + g += gerr[dx] >> 4; + if (g < 0) g = 0; else if (g > 255) g = 255; + b += berr[dx] >> 4; + if (b < 0) b = 0; else if (b > 255) b = 255; + rerr[dx] = lrerr; + gerr[dx] = lgerr; + berr[dx] = lberr; + } + if (r !is lastr || g !is lastg || b !is lastb) { + // moving the variable declarations out seems to make the JDK JIT happier... + for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) { + dr = (destReds[j] & 0xff) - r; + dg = (destGreens[j] & 0xff) - g; + db = (destBlues[j] & 0xff) - b; + distance = dr * dr + dg * dg + db * db; + if (distance < minDistance) { + lastindex = j; + if (distance is 0) break; + minDistance = distance; + } + } + lastr = r; lastg = g; lastb = b; + } + if (ditherEnabled) { + // Floyd-Steinberg error diffusion, cont'd... + int dxm1 = dx - 1, dxp1 = dx + 1; + int acc; + rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr; + rerr[dx] += acc += lrerr + lrerr; + rerr[dxm1] += acc + lrerr + lrerr; + gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr; + gerr[dx] += acc += lgerr + lgerr; + gerr[dxm1] += acc + lgerr + lgerr; + berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr; + berr[dx] += acc += lberr + lberr; + berr[dxm1] += acc + lberr + lberr; + } + + /*** WRITE NEXT PIXEL ***/ + switch (dtype) { + case TYPE_INDEX_8: + destData[dp] = cast(byte) lastindex; + break; + case TYPE_INDEX_4: + if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | lastindex); + else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4)); + break; + case TYPE_INDEX_2: { + int shift = 6 - (dp & 3) * 2; + destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift)); + } break; + case TYPE_INDEX_1_MSB: { + int shift = 7 - (dp & 7); + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift)); + } break; + case TYPE_INDEX_1_LSB: { + int shift = dp & 7; + destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift)); + } break; + default: + } + } + } +} + +/** + * Computes the required channel shift from a mask. + */ +static int getChannelShift(int mask) { + if (mask is 0) return 0; + int i; + for (i = 0; ((mask & 1) is 0) && (i < 32); ++i) { + mask >>>= 1; + } + return i; +} + +/** + * Computes the required channel width (depth) from a mask. + */ +static int getChannelWidth(int mask, int shift) { + if (mask is 0) return 0; + int i; + mask >>>= shift; + for (i = shift; ((mask & 1) !is 0) && (i < 32); ++i) { + mask >>>= 1; + } + return i - shift; +} + +/** + * Extracts a field from packed RGB data given a mask for that field. + */ +static byte getChannelField(int data, int mask) { + static_this(); + int shift = getChannelShift(mask); + return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift]; +} + +/** + * Creates an ImageData containing one band's worth of a gradient filled + * block. Ifvertical
is true, the band must be tiled
+ * horizontally to fill a region, otherwise it must be tiled vertically.
+ *
+ * @param width the width of the region to be filled
+ * @param height the height of the region to be filled
+ * @param vertical if true sweeps from top to bottom, else
+ * sweeps from left to right
+ * @param fromRGB the color to start with
+ * @param toRGB the color to end with
+ * @param redBits the number of significant red bits, 0 for palette modes
+ * @param greenBits the number of significant green bits, 0 for palette modes
+ * @param blueBits the number of significant blue bits, 0 for palette modes
+ * @return the new ImageData
+ */
+static ImageData createGradientBand(
+ int width, int height, bool vertical,
+ RGB fromRGB, RGB toRGB,
+ int redBits, int greenBits, int blueBits) {
+ /* Gradients are drawn as tiled bands */
+ int bandWidth, bandHeight, bitmapDepth;
+ byte[] bitmapData;
+ PaletteData paletteData;
+ /* Select an algorithm depending on the depth of the screen */
+ if (redBits !is 0 && greenBits !is 0 && blueBits !is 0) {
+ paletteData = new PaletteData(0x0000ff00, 0x00ff0000, 0xff000000);
+ bitmapDepth = 32;
+ if (redBits >= 8 && greenBits >= 8 && blueBits >= 8) {
+ /* Precise color */
+ int steps;
+ if (vertical) {
+ bandWidth = 1;
+ bandHeight = height;
+ steps = bandHeight > 1 ? bandHeight - 1 : 1;
+ } else {
+ bandWidth = width;
+ bandHeight = 1;
+ steps = bandWidth > 1 ? bandWidth - 1 : 1;
+ }
+ int bytesPerLine = bandWidth * 4;
+ bitmapData = new byte[bandHeight * bytesPerLine];
+ buildPreciseGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine);
+ buildPreciseGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine);
+ buildPreciseGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine);
+ } else {
+ /* Dithered color */
+ int steps;
+ if (vertical) {
+ bandWidth = (width < 8) ? width : 8;
+ bandHeight = height;
+ steps = bandHeight > 1 ? bandHeight - 1 : 1;
+ } else {
+ bandWidth = width;
+ bandHeight = (height < 8) ? height : 8;
+ steps = bandWidth > 1 ? bandWidth - 1 : 1;
+ }
+ int bytesPerLine = bandWidth * 4;
+ bitmapData = new byte[bandHeight * bytesPerLine];
+ buildDitheredGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine, blueBits);
+ buildDitheredGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine, greenBits);
+ buildDitheredGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine, redBits);
+ }
+ } else {
+ /* Dithered two tone */
+ paletteData = new PaletteData([ fromRGB, toRGB ]);
+ bitmapDepth = 8;
+ int blendi;
+ if (vertical) {
+ bandWidth = (width < 8) ? width : 8;
+ bandHeight = height;
+ blendi = (bandHeight > 1) ? 0x1040000 / (bandHeight - 1) + 1 : 1;
+ } else {
+ bandWidth = width;
+ bandHeight = (height < 8) ? height : 8;
+ blendi = (bandWidth > 1) ? 0x1040000 / (bandWidth - 1) + 1 : 1;
+ }
+ int bytesPerLine = (bandWidth + 3) & -4;
+ bitmapData = new byte[bandHeight * bytesPerLine];
+ if (vertical) {
+ for (int dy = 0, blend = 0, dp = 0; dy < bandHeight;
+ ++dy, blend += blendi, dp += bytesPerLine) {
+ for (int dx = 0; dx < bandWidth; ++dx) {
+ bitmapData[dp + dx] = (blend + DITHER_MATRIX[dy & 7][dx]) <
+ 0x1000000 ? cast(byte)0 : cast(byte)1;
+ }
+ }
+ } else {
+ for (int dx = 0, blend = 0; dx < bandWidth; ++dx, blend += blendi) {
+ for (int dy = 0, dptr = dx; dy < bandHeight; ++dy, dptr += bytesPerLine) {
+ bitmapData[dptr] = (blend + DITHER_MATRIX[dy][dx & 7]) <
+ 0x1000000 ? cast(byte)0 : cast(byte)1;
+ }
+ }
+ }
+ }
+ return new ImageData(bandWidth, bandHeight, bitmapDepth, paletteData, 4, bitmapData);
+}
+
+/*
+ * Fill in gradated values for a color channel
+ */
+static final void buildPreciseGradientChannel(int from, int to, int steps,
+ int bandWidth, int bandHeight, bool vertical,
+ byte[] bitmapData, int dp, int bytesPerLine) {
+ int val = from << 16;
+ int inc = ((to << 16) - val) / steps + 1;
+ if (vertical) {
+ for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
+ bitmapData[dp] = cast(byte)(val >>> 16);
+ val += inc;
+ }
+ } else {
+ for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
+ bitmapData[dp] = cast(byte)(val >>> 16);
+ val += inc;
+ }
+ }
+}
+
+/*
+ * Fill in dithered gradated values for a color channel
+ */
+static final void buildDitheredGradientChannel(int from, int to, int steps,
+ int bandWidth, int bandHeight, bool vertical,
+ byte[] bitmapData, int dp, int bytesPerLine, int bits) {
+ int mask = 0xff00 >>> bits;
+ int val = from << 16;
+ int inc = ((to << 16) - val) / steps + 1;
+ if (vertical) {
+ for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
+ for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
+ int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
+ int temp = val + thresh;
+ if (temp > 0xffffff) bitmapData[dptr] = -1;
+ else bitmapData[dptr] = cast(byte)((temp >>> 16) & mask);
+ }
+ val += inc;
+ }
+ } else {
+ for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
+ for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
+ int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
+ int temp = val + thresh;
+ if (temp > 0xffffff) bitmapData[dptr] = -1;
+ else bitmapData[dptr] = cast(byte)((temp >>> 16) & mask);
+ }
+ val += inc;
+ }
+ }
+}
+
+/**
+ * Renders a gradient onto a GC.
+ * + * This is a GC helper. + *
+ * + * @param gc the GC to render the gradient onto + * @param device the device the GC belongs to + * @param x the top-left x coordinate of the region to be filled + * @param y the top-left y coordinate of the region to be filled + * @param width the width of the region to be filled + * @param height the height of the region to be filled + * @param vertical if true sweeps from top to bottom, else + * sweeps from left to right + * @param fromRGB the color to start with + * @param toRGB the color to end with + * @param redBits the number of significant red bits, 0 for palette modes + * @param greenBits the number of significant green bits, 0 for palette modes + * @param blueBits the number of significant blue bits, 0 for palette modes + */ +static void fillGradientRectangle(GC gc, Device device, + int x, int y, int width, int height, bool vertical, + RGB fromRGB, RGB toRGB, + int redBits, int greenBits, int blueBits) { + /* Create the bitmap and tile it */ + ImageData band = createGradientBand(width, height, vertical, + fromRGB, toRGB, redBits, greenBits, blueBits); + Image image = new Image(device, band); + if ((band.width is 1) || (band.height is 1)) { + gc.drawImage(image, 0, 0, band.width, band.height, x, y, width, height); + } else { + if (vertical) { + for (int dx = 0; dx < width; dx += band.width) { + int blitWidth = width - dx; + if (blitWidth > band.width) blitWidth = band.width; + gc.drawImage(image, 0, 0, blitWidth, band.height, dx + x, y, blitWidth, band.height); + } + } else { + for (int dy = 0; dy < height; dy += band.height) { + int blitHeight = height - dy; + if (blitHeight > band.height) blitHeight = band.height; + gc.drawImage(image, 0, 0, band.width, blitHeight, x, dy + y, band.width, blitHeight); + } + } + } + image.dispose(); +} + +} diff -r d2e87572b721 -r 5f2e72114476 dwt/graphics/ImageDataLoader.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/graphics/ImageDataLoader.d Sat Jan 26 19:05:32 2008 +0100 @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit+ * Currently supported image formats are: + *
ImageLoaders
can be used to:
+ * imageLoaderListeners
field.
+ */
+void reset() {
+ data = null;
+ logicalScreenWidth = 0;
+ logicalScreenHeight = 0;
+ backgroundPixel = -1;
+ repeatCount = 1;
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * specified input stream. Throws an error if either an error
+ * occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @return an array of ImageData
objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException ImageData
objects from the
+ * file with the specified name. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @return an array of ImageData
objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException IMAGE_BMP
IMAGE_BMP_RLE
IMAGE_GIF
IMAGE_ICO
IMAGE_JPEG
IMAGE_PNG
IMAGE_BMP
IMAGE_BMP_RLE
IMAGE_GIF
IMAGE_ICO
IMAGE_JPEG
IMAGE_PNG
+ * An ImageLoaderListener should be added before invoking
+ * one of the receiver's load methods. The listener's
+ * imageDataLoaded
method is called when image
+ * data has been partially loaded, as is supported by interlaced
+ * GIF/PNG or progressive JPEG images.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException
true
if the receiver has image loader
+ * listeners, and false
otherwise.
+ *
+ * @return true
if there are ImageLoaderListener
s, and false
otherwise
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ * @see #removeImageLoaderListener(ImageLoaderListener)
+ */
+public bool hasListeners() {
+ return imageLoaderListeners.length > 0;
+}
+
+/**
+ * Notifies all image loader listeners that an image loader event
+ * has occurred. Pass the specified event object to each listener.
+ *
+ * @param event the ImageLoaderEvent
to send to each ImageLoaderListener
+ */
+public void notifyListeners(ImageLoaderEvent event) {
+ if (!hasListeners()) return;
+ foreach( listener; imageLoaderListeners ){
+ listener.imageDataLoaded(event);
+ }
+}
+
+}
diff -r d2e87572b721 -r 5f2e72114476 dwt/graphics/ImageLoaderEvent.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageLoaderEvent.d Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit + * Notes: + *
endOfImage
flag in the event will be set to true
+ * after each individual image is loaded.endOfImage
flag is false, then this is a
+ * partially complete copy of the current ImageData
,
+ * otherwise this is a completely loaded ImageData
+ */
+ public ImageData imageData;
+
+ /**
+ * the zero-based count of image data increments -- this is
+ * equivalent to the number of events that have been generated
+ * while loading a particular image
+ */
+ public int incrementCount;
+
+ /**
+ * If this flag is true, then the current image data has been
+ * completely loaded, otherwise the image data is only partially
+ * loaded, and further ImageLoader events will occur unless an
+ * exception is thrown
+ */
+ public bool endOfImage;
+
+ //static final long serialVersionUID = 3257284738325558065L;
+
+/**
+ * Constructs a new instance of this class given the event source and
+ * the values to store in its fields.
+ *
+ * @param source the ImageLoader that was loading when the event occurred
+ * @param imageData the image data for the event
+ * @param incrementCount the image data increment for the event
+ * @param endOfImage the end of image flag for the event
+ */
+public this(ImageLoader source, ImageData imageData, int incrementCount, bool endOfImage) {
+ super(source);
+ this.imageData = imageData;
+ this.incrementCount = incrementCount;
+ this.endOfImage = endOfImage;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+ return Format( "ImageLoaderEvent {source={} imageData={} incrementCount={} endOfImage={}}", source, imageData, incrementCount, endOfImage); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+}
+
+}
diff -r d2e87572b721 -r 5f2e72114476 dwt/graphics/ImageLoaderListener.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageLoaderListener.d Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit
+ * After creating an instance of a class that implements
+ * this interface it can be added to an image loader using the
+ * addImageLoaderListener
method and removed using
+ * the removeImageLoaderListener
method. When
+ * image data is either partially or completely loaded, this
+ * method will be invoked.
+ *
+ * The timing of when this method is called varies depending on + * the format of the image being loaded. + *
+ * + * @param e an event containing information about the image loading operation + */ +public void imageDataLoaded(ImageLoaderEvent e); + +} diff -r d2e87572b721 -r 5f2e72114476 dwt/internal/gdip/Gdip.d --- a/dwt/internal/gdip/Gdip.d Sat Jan 26 16:45:05 2008 +0100 +++ b/dwt/internal/gdip/Gdip.d Sat Jan 26 19:05:32 2008 +0100 @@ -12,40 +12,8 @@ import dwt.internal.Library; import dwt.internal.Platform; - -public struct RectF { - public float X; - public float Y; - public float Width; - public float Height; -} +import dwt.internal.win32.OS; -public struct Rect { - public int X; - public int Y; - public int Width; - public int Height; -} -public struct PointF { - public float X; - public float Y; -} - -public struct ColorPalette { - public int Flags; - public int Count; - public int[1] Entries; -} -public struct BitmapData { - public int Width; - public int Height; - public int Stride; - public int PixelFormat; - public void* Scan0; - public int Reserved; -} - -struct Matrix{} alias extern(Windows) void function(int level, char *message) DebugEventProc; @@ -194,6 +162,49 @@ public static const int WrapModeClamp = 4; +public struct RectF { + public float X; + public float Y; + public float Width; + public float Height; +} + +public struct Rect { + public int X; + public int Y; + public int Width; + public int Height; +} +public struct PointF { + public float X; + public float Y; +} + +public struct ColorPalette { + public int Flags; + public int Count; + public int[1] Entries; +} +public struct BitmapData { + public int Width; + public int Height; + public int Stride; + public int PixelFormat; + public void* Scan0; + public int Reserved; +} + +struct Matrix{} +struct Bitmap{} +struct Image{} +struct Pen{} +struct Brush{} +struct Font{} +struct StringFormat{} +struct Region{} +struct GraphicsPath{} +struct Graphics{} +struct ImageAttributes{} public alias API.GdiplusStartup GdiplusStartup; public alias API.GdiplusShutdown GdiplusShutdown; @@ -210,6 +221,77 @@ public static extern(Windows) int Matrix_TransformPoints(Matrix*,PointF*, int); public static extern(Windows) int Matrix_Translate(Matrix*,float, float, float); + public static extern(Windows) Bitmap* Bitmap_new(HBITMAP hbm, HPALETTE hpal); + public static extern(Windows) Bitmap* Bitmap_new(HICON hicon); + public static extern(Windows) Bitmap* Bitmap_new(int width, int height, int stride, int format, byte* scan0); + public static extern(Windows) Bitmap* Bitmap_new(wchar* filename, int useIcm); + public static extern(Windows) void Bitmap_delete(Bitmap* bitmap); + // colorBackground is C++ &-reference Color + public static extern(Windows) int Bitmap_GetHBITMAP(Bitmap*bitmap, void* colorBackground, HBITMAP* hbmReturn); + public static extern(Windows) int Bitmap_GetHICON(Bitmap* bitmap, HICON* hicon); + public static extern(Windows) int Bitmap_LockBits(Bitmap* bitmap, Rect* rect, int flags, int pixelFormat, BitmapData* lockedBitmapData); + public static extern(Windows) int Bitmap_UnlockBits(Bitmap* bitmap, BitmapData* lockedBitmapData); + + public static extern(Windows) BitmapData* BitmapData_new(); + public static extern(Windows) void BitmapData_delete(BitmapData* bitmapData); + + public static extern(Windows) int Image_GetLastStatus(Image* image); + public static extern(Windows) int Image_GetPixelFormat(Image* image); + public static extern(Windows) uint Image_GetWidth(Image* image); + public static extern(Windows) uint Image_GetHeight(Image* image); + public static extern(Windows) int Image_GetPalette(Image* image, ColorPalette* palette, int size); + public static extern(Windows) int Image_GetPaletteSize(Image* image); + +alias extern(Windows) int function(void*) ImageAbort; +alias ImageAbort DrawImageAbort; + public static extern(Windows) Graphics* Graphics_new(HDC hdc); + public static extern(Windows) void Graphics_delete(Graphics* graphics); + public static extern(Windows) int Graphics_DrawArc(Graphics* graphics, Pen* pen, int x, int y, int width, int height, float startAngle, float sweepAngle); + public static extern(Windows) int Graphics_DrawEllipse(Graphics* graphics, Pen* pen, int x, int y, int width, int height); + public static extern(Windows) int Graphics_DrawImage(Graphics* graphics, Image* image, int x, int y); + public static extern(Windows) int Graphics_DrawImage(Graphics* graphics, Image* image, Rect* destRect, int srcx, int srcy, int srcwidth, int srcheight, int srcUnit, ImageAttributes* imageAttributes, DrawImageAbort callback, void* callbackData); + public static extern(Windows) int Graphics_DrawLine(Graphics* graphics, Pen* pen, int x1, int y1, int x2, int y2); + public static extern(Windows) int Graphics_DrawLines(Graphics* graphics, Pen* pen, PointF* points, int count); + public static extern(Windows) int Graphics_DrawPath(Graphics* graphics, Pen* pen, GraphicsPath* path); + public static extern(Windows) int Graphics_DrawPolygon(Graphics* graphics, Pen* pen, int[] points, int count); + public static extern(Windows) int Graphics_DrawRectangle(Graphics* graphics, Pen* pen, int x, int y, int width, int height); + public static extern(Windows) int Graphics_DrawString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, Brush* brush); + public static extern(Windows) int Graphics_DrawString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, StringFormat* format, Brush* brush); + public static extern(Windows) int Graphics_FillEllipse(Graphics* graphics, Brush* brush, int x, int y, int width, int height); + public static extern(Windows) int Graphics_FillPath(Graphics* graphics, Brush* brush, GraphicsPath* path); + public static extern(Windows) void Graphics_Flush(Graphics* graphics, int intention); + public static extern(Windows) int Graphics_FillPie(Graphics* graphics, Brush* brush, int x, int y, int width, int height, float startAngle, float sweepAngle); + public static extern(Windows) int Graphics_FillPolygon(Graphics* graphics, Brush* brush, int[] points, int count, int fillMode); + public static extern(Windows) int Graphics_FillRectangle(Graphics* graphics, Brush* brush, int x, int y, int width, int height); + public static extern(Windows) int Graphics_GetClipBounds(Graphics* graphics, RectF rect); + public static extern(Windows) int Graphics_GetClipBounds(Graphics* graphics, Rect rect); + public static extern(Windows) int Graphics_GetClip(Graphics* graphics, Region* region); + public static extern(Windows) HDC Graphics_GetHDC(Graphics* graphics); + public static extern(Windows) void Graphics_ReleaseHDC(Graphics* graphics, HDC hdc); + public static extern(Windows) int Graphics_GetInterpolationMode(Graphics* graphics); + public static extern(Windows) int Graphics_GetSmoothingMode(Graphics* graphics); + public static extern(Windows) int Graphics_GetTextRenderingHint(Graphics* graphics); + public static extern(Windows) int Graphics_GetTransform(Graphics* graphics, Matrix* matrix); + public static extern(Windows) int Graphics_GetVisibleClipBounds(Graphics* graphics, Rect rect); + public static extern(Windows) int Graphics_MeasureString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, RectF boundingBox); + public static extern(Windows) int Graphics_MeasureString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, StringFormat* format, RectF boundingBox); + public static extern(Windows) int Graphics_ResetClip(Graphics* graphics); + public static extern(Windows) int Graphics_Restore(Graphics* graphics, int gstate); + public static extern(Windows) int Graphics_Save(Graphics* graphics); + public static extern(Windows) int Graphics_ScaleTransform(Graphics* graphics, float sx, float sy, int order); + public static extern(Windows) int Graphics_SetClip(Graphics* graphics, HRGN hrgn, int combineMode); + public static extern(Windows) int Graphics_SetClip(Graphics* graphics, GraphicsPath* path); + public static extern(Windows) int Graphics_SetClip(Graphics* graphics, Rect rect, int combineMode); + public static extern(Windows) int Graphics_SetCompositingQuality(Graphics* graphics, int compositingQuality); + public static extern(Windows) int Graphics_SetPageUnit(Graphics* graphics, int unit); + public static extern(Windows) int Graphics_SetPixelOffsetMode(Graphics* graphics, int pixelOffsetMode); + public static extern(Windows) int Graphics_SetSmoothingMode(Graphics* graphics, int smoothingMode); + public static extern(Windows) int Graphics_SetTransform(Graphics* graphics, Matrix* matrix); + public static extern(Windows) int Graphics_SetInterpolationMode(Graphics* graphics, int mode); + public static extern(Windows) int Graphics_SetTextRenderingHint(Graphics* graphics, int mode); + public static extern(Windows) int Graphics_TranslateTransform(Graphics* graphics, float dx, float dy, int order); + + /++ /** 64 bit */ public static final native int ColorPalette_sizeof();