/*
 * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include <stdlib.h>
#include <jni.h>
#include <jlong.h>
#include "X11SurfaceData.h"
#include "Region.h"

JNIEXPORT void JNICALL
Java_sun_java2d_x11_X11PMBlitLoops_nativeBlit
    (JNIEnv *env, jobject joSelf,
     jlong srcData, jlong dstData,
     jlong gc, jobject clip,
     jint srcx, jint srcy,
     jint dstx, jint dsty,
     jint width, jint height)
{
#ifndef HEADLESS
    X11SDOps *srcXsdo, *dstXsdo;
    SurfaceDataBounds span, srcBounds;
    RegionData clipInfo;
    GC xgc;

    if (width <= 0 || height <= 0) {
        return;
    }

    srcXsdo = (X11SDOps *)jlong_to_ptr(srcData);
    if (srcXsdo == NULL) {
        return;
    }
    dstXsdo = (X11SDOps *)jlong_to_ptr(dstData);
    if (dstXsdo == NULL) {
        return;
    }
    if (Region_GetInfo(env, clip, &clipInfo)) {
        return;
    }

    xgc = (GC)gc;
    if (xgc == NULL) {
        return;
    }

#ifdef MITSHM
    if (srcXsdo->isPixmap) {
        X11SD_UnPuntPixmap(srcXsdo);
    }
#endif /* MITSHM */

    /* clip the source rect to the source pixmap's dimensions */
    srcBounds.x1 = srcx;
    srcBounds.y1 = srcy;
    srcBounds.x2 = srcx + width;
    srcBounds.y2 = srcy + height;
    SurfaceData_IntersectBoundsXYXY(&srcBounds,
                                    0, 0, srcXsdo->pmWidth, srcXsdo->pmHeight);
    span.x1 = dstx;
    span.y1 = dsty;
    span.x2 = dstx + width;
    span.y2 = dsty + height;

    /* intersect the source and dest rects */
    SurfaceData_IntersectBlitBounds(&srcBounds, &span,
                                    dstx - srcx, dsty - srcy);
    srcx = srcBounds.x1;
    srcy = srcBounds.y1;
    dstx = span.x1;
    dsty = span.y1;

    if (srcXsdo->bitmask != 0) {
        XSetClipOrigin(awt_display, xgc, dstx - srcx, dsty - srcy);
        XSetClipMask(awt_display, xgc, srcXsdo->bitmask);
    }

    Region_IntersectBounds(&clipInfo, &span);
    if (!Region_IsEmpty(&clipInfo)) {
        Region_StartIteration(env, &clipInfo);
        srcx -= dstx;
        srcy -= dsty;
        while (Region_NextIteration(&clipInfo, &span)) {
            XCopyArea(awt_display, srcXsdo->drawable, dstXsdo->drawable, xgc,
                      srcx + span.x1, srcy + span.y1,
                      span.x2 - span.x1, span.y2 - span.y1,
                      span.x1, span.y1);
        }
        Region_EndIteration(env, &clipInfo);
    }

    if (srcXsdo->bitmask != 0) {
        XSetClipMask(awt_display, xgc, None);
    }

#ifdef MITSHM
    if (srcXsdo->shmPMData.usingShmPixmap) {
        srcXsdo->shmPMData.xRequestSent = JNI_TRUE;
    }
#endif /* MITSHM */
    X11SD_DirectRenderNotify(env, dstXsdo);
#endif /* !HEADLESS */
}

JNIEXPORT void JNICALL
Java_sun_java2d_x11_X11PMBlitBgLoops_nativeBlitBg
    (JNIEnv *env, jobject joSelf,
     jlong srcData, jlong dstData,
     jlong xgc, jint pixel,
     jint srcx, jint srcy,
     jint dstx, jint dsty,
     jint width, jint height)
{
#ifndef HEADLESS
    X11SDOps *srcXsdo, *dstXsdo;
    GC dstGC;
    SurfaceDataBounds dstBounds, srcBounds;
    Drawable srcDrawable;

    if (width <= 0 || height <= 0) {
        return;
    }

    srcXsdo = (X11SDOps *)jlong_to_ptr(srcData);
    if (srcXsdo == NULL) {
        return;
    }
    dstXsdo = (X11SDOps *)jlong_to_ptr(dstData);
    if (dstXsdo == NULL) {
        return;
    }

    dstGC = (GC)xgc;
    if (dstGC == NULL) {
        return;
    }

#ifdef MITSHM
    if (srcXsdo->isPixmap) {
        X11SD_UnPuntPixmap(srcXsdo);
    }
#endif /* MITSHM */

    srcDrawable = srcXsdo->GetPixmapWithBg(env, srcXsdo, pixel);
    if (srcDrawable == 0) {
        return;
    }

    /* clip the source rect to the source pixmap's dimensions */
    srcBounds.x1 = srcx;
    srcBounds.y1 = srcy;
    srcBounds.x2 = srcx + width;
    srcBounds.y2 = srcy + height;
    SurfaceData_IntersectBoundsXYXY(&srcBounds,
                                    0, 0, srcXsdo->pmWidth, srcXsdo->pmHeight);
    dstBounds.x1 = dstx;
    dstBounds.y1 = dsty;
    dstBounds.x2 = dstx + width;
    dstBounds.y2 = dsty + height;

    /* intersect the source and dest rects */
    SurfaceData_IntersectBlitBounds(&srcBounds, &dstBounds,
                                    dstx - srcx, dsty - srcy);
    srcx = srcBounds.x1;
    srcy = srcBounds.y1;
    dstx = dstBounds.x1;
    dsty = dstBounds.y1;
    width = srcBounds.x2 - srcBounds.x1;
    height = srcBounds.y2 - srcBounds.y1;

    /* do an unmasked copy as we've already filled transparent
       pixels of the source image with the desired color */
    XCopyArea(awt_display, srcDrawable, dstXsdo->drawable, dstGC,
              srcx, srcy, width, height, dstx, dsty);

    srcXsdo->ReleasePixmapWithBg(env, srcXsdo);
    X11SD_DirectRenderNotify(env, dstXsdo);
#endif /* !HEADLESS */
}

/*
 * Class:     sun_java2d_x11_X11PMBlitLoops
 * Method:    updateBitmask
 * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;)V
 */
JNIEXPORT void JNICALL
Java_sun_java2d_x11_X11PMBlitLoops_updateBitmask
    (JNIEnv *env, jclass xpmbl, jobject srcsd, jobject dstsd, jboolean isICM)
{
#ifndef HEADLESS
    SurfaceDataOps *srcOps = SurfaceData_GetOps(env, srcsd);
    X11SDOps *xsdo = (X11SDOps *) SurfaceData_GetOps(env, dstsd);
    SurfaceDataRasInfo srcInfo;

    int flags;
    int screen;
    int width;
    int height;
    jint srcScan, dstScan;
    int rowCount;
    unsigned char *pDst;
    XImage *image;
    GC xgc;

    if (srcOps == NULL || xsdo == NULL) {
        JNU_ThrowNullPointerException(env, "Null BISD in updateMaskRegion");
        return;
    }

    AWT_LOCK();

    screen = xsdo->configData->awt_visInfo.screen;
    width = xsdo->pmWidth;
    height = xsdo->pmHeight;

    if (xsdo->bitmask == 0) {
        /* create the bitmask if it is not yet created */
        xsdo->bitmask = XCreatePixmap(awt_display,
                                      RootWindow(awt_display, screen),
                                      width, height, 1);
        if (xsdo->bitmask == 0) {
            AWT_UNLOCK();
            JNU_ThrowOutOfMemoryError(env,
                                      "Cannot create bitmask for "
                                      "offscreen surface");
            return;
        }
    }

    /* Create a bitmask image and then blit it to the pixmap. */
    image = XCreateImage(awt_display, DefaultVisual(awt_display, screen),
                         1, XYBitmap, 0, NULL, width, height, 32, 0);
    if (image == NULL) {
        AWT_UNLOCK();
        JNU_ThrowOutOfMemoryError(env, "Cannot allocate bitmask for mask");
        return;
    }
    dstScan = image->bytes_per_line;
    image->data = malloc(dstScan * height);
    if (image->data == NULL) {
        XFree(image);
        AWT_UNLOCK();
        JNU_ThrowOutOfMemoryError(env, "Cannot allocate bitmask for mask");
        return;
    }
    pDst = (unsigned char *)image->data;

    srcInfo.bounds.x1 = 0;
    srcInfo.bounds.y1 = 0;
    srcInfo.bounds.x2 = width;
    srcInfo.bounds.y2 = height;

    flags = (isICM ? (SD_LOCK_LUT | SD_LOCK_READ) : SD_LOCK_READ);
    if (srcOps->Lock(env, srcOps, &srcInfo, flags) != SD_SUCCESS) {
        XDestroyImage(image);
        AWT_UNLOCK();
        return;
    }
    srcOps->GetRasInfo(env, srcOps, &srcInfo);

    rowCount = height;
    if (isICM) {
        unsigned char *pSrc;
        jint *srcLut;

        srcScan = srcInfo.scanStride;
        srcLut = srcInfo.lutBase;
        pSrc = (unsigned char *)srcInfo.rasBase;

        if (image->bitmap_bit_order == MSBFirst) {
            do {
                int x = 0, bx = 0;
                unsigned int pix = 0;
                unsigned int bit = 0x80;
                unsigned char *srcPixel = pSrc;
                do {
                    if (bit == 0) {
                        pDst[bx++] = (unsigned char)pix;
                        pix = 0;
                        bit = 0x80;
                    }
                    pix |= bit & (srcLut[*srcPixel++] >> 31);
                    bit >>= 1;
                } while (++x < width);
                pDst[bx] = (unsigned char)pix;
                pDst += dstScan;
                pSrc = (unsigned char *) (((intptr_t)pSrc) + srcScan);
            } while (--rowCount > 0);
        } else {
            do {
                int x = 0, bx = 0;
                unsigned int pix = 0;
                unsigned int bit = 1;
                unsigned char *srcPixel = pSrc;
                do {
                    if ((bit >> 8) != 0) {
                        pDst[bx++] = (unsigned char) pix;
                        pix = 0;
                        bit = 1;
                    }
                    pix |= bit & (srcLut[*srcPixel++] >> 31);
                    bit <<= 1;
                } while (++x < width);
                pDst[bx] = (unsigned char) pix;
                pDst += dstScan;
                pSrc = (unsigned char *) (((intptr_t)pSrc) + srcScan);
            } while (--rowCount > 0);
        }
    } else /*DCM with ARGB*/ {
        unsigned int *pSrc;

        /* this is a number of pixels in a row, not number of bytes */
        srcScan = srcInfo.scanStride;
        pSrc = (unsigned int *)srcInfo.rasBase;

        if (image->bitmap_bit_order == MSBFirst) {
            do {
                int x = 0, bx = 0;
                unsigned int pix = 0;
                unsigned int bit = 0x80;
                int *srcPixel = (int *) pSrc;
                do {
                    if (bit == 0) {
                        /* next word */
                        pDst[bx++] = (unsigned char)pix;
                        pix = 0;
                        bit = 0x80;
                    }
                    if (*srcPixel++ & 0xff000000) {
                        /* if src pixel is opaque, set the bit in the bitmap */
                        pix |= bit;
                    }
                    bit >>= 1;
                } while (++x < width);
                /* last pixels in a row */
                pDst[bx] = (unsigned char)pix;

                pDst += dstScan;
                pSrc = (unsigned int *) (((intptr_t)pSrc) + srcScan);
            } while (--rowCount > 0);
        } else {
            do {
                int x = 0, bx = 0;
                unsigned int pix = 0;
                unsigned int bit = 1;
                int *srcPixel = (int *) pSrc;
                do {
                    if ((bit >> 8) != 0) {
                        pDst[bx++] = (unsigned char)pix;
                        pix = 0;
                        bit = 1;
                    }
                    if (*srcPixel++ & 0xff000000) {
                        /* if src pixel is opaque, set the bit in the bitmap */
                        pix |= bit;
                    }
                    bit <<= 1;
                } while (++x < width);
                pDst[bx] = (unsigned char)pix;
                pDst += dstScan;
                pSrc = (unsigned int *) (((intptr_t)pSrc) + srcScan);
            } while (--rowCount > 0);
        }
    }
    SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);

    xgc = XCreateGC(awt_display, xsdo->bitmask, 0L, NULL);
    XSetForeground(awt_display, xgc, 1);
    XSetBackground(awt_display, xgc, 0);
    XPutImage(awt_display, xsdo->bitmask, xgc,
              image, 0, 0, 0, 0, width, height);

    XFreeGC(awt_display, xgc);
    XDestroyImage(image);

    AWT_UNLOCK();
#endif /* !HEADLESS */
}
