root/src/libs/print/libprint/ValidRect.cpp
/*
 * ValidRect.cpp
 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
 * Copyright 2005 Michael Pfeiffer. All Rights Reserved.
 * - Rewrote get_valid_rect from scratch.
 */

#include <Bitmap.h>
#include "ValidRect.h"

#define INLINE inline

class BoundsCalculator
{
public:
        bool getValidRect(BBitmap *bitmap, RECT *rect);
        
private:
        const uchar *fBits;
        int fBPR;
        int fLeft;
        int fRight;
        int fTop;
        int fBottom;
        int fWidth;
        
        int fLeftBound;
        int fRightBound;

        INLINE bool isEmpty(const rgb_color *pixel);

        INLINE bool isRowEmpty(const rgb_color *row);

        INLINE const uchar *getRow(int x, int y);

        int getTop();
        
        int getBottom();
        
        INLINE void updateLeftBound(const rgb_color *row);
        INLINE void updateRightBound(const rgb_color *row);
};


bool 
BoundsCalculator::isEmpty(const rgb_color *pixel)
{
        return pixel->red == 0xff && pixel->green == 0xff && pixel->blue == 0xff;
}


bool 
BoundsCalculator::isRowEmpty(const rgb_color *row)
{
        for (int x = 0; x < fWidth; x ++) {
                if (!isEmpty(row)) {
                        return false;
                }
                row ++;
        }
        return true;
}


const uchar *
BoundsCalculator::getRow(int x, int y)
{
        return fBits + x + fBPR * y;
}


int 
BoundsCalculator::getTop()
{       
        const uchar* row = getRow(fLeft, fTop);

        int top;
        for (top = fTop; top <= fBottom; top ++) {
                if (!isRowEmpty((const rgb_color*)row)) {
                        break;
                }
                row += fBPR;
        }
        
        return top;
}


int
BoundsCalculator::getBottom()
{
        const uchar *row = getRow(fLeft, fBottom);

        int bottom;
        for (bottom = fBottom; bottom >= fTop; bottom --) {
                if (!isRowEmpty((const rgb_color*)row)) {
                        break;
                }
                row -= fBPR;
        }
        
        return bottom;
}


void
BoundsCalculator::updateLeftBound(const rgb_color *row)
{
        for (int x = fLeft; x < fLeftBound; x ++) {
                if (!isEmpty(row)) {
                        fLeftBound = x;
                        return;
                }
                row ++;
        }
}


void
BoundsCalculator::updateRightBound(const rgb_color *row)
{
        row += fWidth - 1;
        for (int x = fRight; x > fRightBound; x --) {
                if (!isEmpty(row)) {
                        fRightBound = x;
                        return;
                }
                row --;
        }
}


// returns false if the bitmap is empty or has wrong color space.
bool
BoundsCalculator::getValidRect(BBitmap *bitmap, RECT *rect)
{
        enum {
                kRectIsInvalid = false,
                kRectIsEmpty = false,
                kRectIsValid = true
        };
        
        switch (bitmap->ColorSpace()) {
                case B_RGB32:
                case B_RGB32_BIG:
                        break;
                default:
                        return kRectIsInvalid;
                        break;
        };

        // initialize member variables
        fBits = (uchar*)bitmap->Bits();
        fBPR  = bitmap->BytesPerRow();
        
        fLeft   = rect->left;   
        fRight  = rect->right;
        fTop    = rect->top;
        fBottom = rect->bottom;
        
        fWidth = fRight - fLeft + 1;

        // get top bound
        fTop = getTop();
        if (fTop > fBottom) {
                return kRectIsEmpty;
        }
        
        // get bottom bound
        fBottom = getBottom();
        
        // calculate left and right bounds
        fLeftBound = fRight + 1;
        fRightBound = fLeft - 1;
        
        const uchar *row = getRow(fLeft, fTop);
        for (int y = fTop; y <= fBottom; y ++) {
                updateLeftBound((const rgb_color*)row);
                updateRightBound((const rgb_color*)row);
                if (fLeft == fLeftBound && fRight == fRightBound) {
                        break;
                }
                row += fBPR;
        }
        
        // return bounds in rectangle
        rect->left = fLeftBound;
        rect->right = fRightBound; 
        rect->top = fTop;
        rect->bottom = fBottom;
                
        return kRectIsValid;
}


bool get_valid_rect(BBitmap *a_bitmap, RECT *rc)
{
        BoundsCalculator calculator;
        return calculator.getValidRect(a_bitmap, rc);
}


int color_space2pixel_depth(color_space cs)
{
        int pixel_depth;

        switch (cs) {
        case B_GRAY1:           /* Y0[0],Y1[0],Y2[0],Y3[0],Y4[0],Y5[0],Y6[0],Y7[0]      */
                pixel_depth = 1;
                break;
        case B_GRAY8:           /* Y[7:0]                                                                                       */
        case B_CMAP8:           /* D[7:0]                                                                                       */
                pixel_depth = 8;
                break;
        case B_RGB15:           /* G[2:0],B[4:0]           -[0],R[4:0],G[4:3]                   */
        case B_RGB15_BIG:       /* -[0],R[4:0],G[4:3]  G[2:0],B[4:0]                            */
                pixel_depth = 16;
                break;
        case B_RGB32:           /* B[7:0]  G[7:0]  R[7:0]  -[7:0]                                       */
        case B_RGB32_BIG:       /* -[7:0]  R[7:0]  G[7:0]  B[7:0]                                       */
                pixel_depth = 32;
                break;
        default:
                pixel_depth = 0;
                break;
        }
        return pixel_depth;
}