/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Reformat Print Shop graphics.
 *
 * The "image editor" in Print Shop GS shows a nearly square image.  It looks
 * like it doubles with width and triples the height, resulting in a
 * 176x156 image.  We could do that here, but that would make it harder to
 * manipulate the basic pixels if a different aspect ratio were desired.
 * (May want to make that an option someday.)
 */
#include "StdAfx.h"
#include "PrintShop.h"

/*
 * Decide whether or not we want to handle this file.
 */
void ReformatPrintShop::Examine(ReformatHolder* pHolder)
{
    ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
    long fileType = pHolder->GetFileType();
    long auxType = pHolder->GetAuxType();
    long length = pHolder->GetSourceLen(ReformatHolder::kPartData);
    bool relaxed;

    relaxed = pHolder->GetOption(ReformatHolder::kOptRelaxGfxTypeCheck) != 0;

    if (length == 576 && fileType == kTypeBIN) {
        /* PrintShop monochrome */
        if (auxType == 0x4800 || auxType == 0x5800 ||
            auxType == 0x6800 || auxType == 0x7800)
            applies = ReformatHolder::kApplicYes;
        else
            applies = ReformatHolder::kApplicMaybe; // below monitor listing
    } else if (length == 1716) {
        /* PrintShop GS color */
        if (fileType == 0xf8) {
            if (auxType == 0xc323)
                applies = ReformatHolder::kApplicYes;
            else
                applies = ReformatHolder::kApplicProbably;
        } else if (fileType == kTypeBIN && relaxed)
            applies = ReformatHolder::kApplicMaybe;
    } else if (length == 572) {
        /* PrintShop GS monochrome */
        if (fileType == 0xf8 && auxType == 0xc313)
            applies = ReformatHolder::kApplicYes;
    }

    pHolder->SetApplic(ReformatHolder::kReformatPrintShop, applies,
        ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}

/*
 * Convert Print Shop clip art into a DIB.
 */
int ReformatPrintShop::Process(const ReformatHolder* pHolder,
    ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
    ReformatOutput* pOutput)
{
    MyDIBitmap* pDib = NULL;
    const uint8_t* srcBuf = pHolder->GetSourceBuf(part);
    long srcLen = pHolder->GetSourceLen(part);

    if (srcLen == 572 || srcLen == 576) {
        pDib = ConvertBW(srcBuf);
    } else if (srcLen == 1716) {
        pDib = ConvertColor(srcBuf);
    } else {
        LOGI("PS shouldn't be here (len=%ld)", srcLen);
        return -1;
    }

    if (pDib == NULL) {
        LOGI("DIB creation failed");
        return -1;
    }

    SetResultBuffer(pOutput, pDib);
    return 0;
}

/*
 * Convert a B&W "classic" Print Shop graphic.
 *
 * This does a "straight" conversion without half-pixel shifting
 * or other monkey business.
 *
 * The file is a linear 1-bit 88x52 image, with four extra bytes of data
 * at the end.
 */
MyDIBitmap* ReformatPrintShop::ConvertBW(const uint8_t* srcBuf)
{
    MyDIBitmap* pDib = new MyDIBitmap;
    uint8_t* outBuf;
    uint8_t* ptr;
    int pitch;
    int x, y;

    if (pDib == NULL)
        return NULL;

    RGBQUAD colorConv[2];
    colorConv[0].rgbRed = colorConv[0].rgbGreen = colorConv[0].rgbBlue = 255;
    colorConv[1].rgbRed = colorConv[1].rgbGreen = colorConv[1].rgbBlue = 0;
    colorConv[0].rgbReserved = colorConv[1].rgbReserved = 0;

    outBuf = (uint8_t*) pDib->Create(kWidth, kHeight, 1, 2);
    if (outBuf == NULL) {
        delete pDib;
        pDib = NULL;
        goto bail;
    }
    pDib->SetColorTable(colorConv);

    pitch = pDib->GetPitch();

    /* build it in standard upside-down Windows format */
    for (y = kHeight-1; y >= 0; y--) {
        ptr = outBuf + y * pitch;
        for (x = 0; x < kWidth/8; x++)
            *ptr++ = *srcBuf++;
    }

bail:
    return pDib;
}

/*
 * The format is similar to the B&W version, but instead of one bitmap
 * there are three.  They're sorta-kinda CMY:
 *
 * >[...]                         There are 3 bit maps for the graphic. The
 * >first is yellow, the second is magenta and the third is cyan. The other
 * >colors are from combinations. Yellow and magenta is orange. Yellow and cyan
 * >is green. Magenta and cyan are purple. All three is black.
 *
 * It appears, based on running Print Shop GS in an emulator, that "cyan"
 * is actually 100% blue and "magenta" is 100% red.  The values in the color
 * table below come from a screen capture of KEGS.
 */
MyDIBitmap* ReformatPrintShop::ConvertColor(const uint8_t* srcBuf)
{
    MyDIBitmap* pDib = new MyDIBitmap;
    uint8_t* outBuf;
    uint8_t* ptr;
    uint8_t outVal;
    uint16_t yellow, magenta, cyan;
    int pitch;
    int x, y, bit;
    static const RGBQUAD kColorConv[8] = {
        /* blue, green, red, reserved  YMC */
        { 0xff, 0xff, 0xff },       // 000 white
        { 0xff, 0x00, 0x00 },       // 001 cyan (blue)
        { 0x00, 0x00, 0xff },       // 010 magenta (red)
        { 0xcc, 0x00, 0xcc },       // 011 purple
        { 0x00, 0xff, 0xff },       // 100 yellow
        { 0x00, 0xff, 0x00 },       // 101 green
        { 0x00, 0x66, 0xff },       // 110 orange
        { 0x00, 0x00, 0x00 },       // 111 black
    };

    if (pDib == NULL)
        return NULL;

    outBuf = (uint8_t*) pDib->Create(kWidth, kHeight, 4, 8);
    if (outBuf == NULL) {
        delete pDib;
        pDib = NULL;
        goto bail;
    }
    pDib->SetColorTable(kColorConv);

    pitch = pDib->GetPitch();

    /*
     * Build it in standard upside-down Windows format.
     *
     * We pre-shift the yellow/magenta/cyan values into offsetting positions
     * to save ourselves a shift each time through the loop.
     */
    for (y = kHeight-1; y >= 0; y--) {
        ptr = outBuf + y * pitch;
        for (x = 0; x < kWidth/8; x++) {
            yellow = *srcBuf << 2;
            magenta = *(srcBuf + (kWidth/8)*kHeight) << 1;
            cyan = *(srcBuf + (kWidth/8)*kHeight *2);

            /* each 3-bit combo turns into a 4-bit index, 2 per output byte */
            for (bit = 0; bit < 4; bit++) {
                outVal = ((yellow & 0x200) | (magenta & 0x100) | (cyan & 0x80)) >> 3;
                yellow <<= 1;
                magenta <<= 1;
                cyan <<= 1;
                outVal |= ((yellow & 0x200) | (magenta & 0x100) | (cyan & 0x80)) >> 7;
                yellow <<= 1;
                magenta <<= 1;
                cyan <<= 1;
                *ptr++ = outVal;
            }

            srcBuf++;
        }
    }

bail:
    return pDib;
}
