root/src/libs/print/libprint/HalftoneView.cpp
#include "HalftoneView.h"

#include <Bitmap.h>
#include <StringView.h>


HalftonePreviewView::HalftonePreviewView(BRect frame, const char* name,
        uint32 resizeMask, uint32 flags)
        :
        BView(frame, name, resizeMask, flags)
{
}


void
HalftonePreviewView::Preview(float gamma, float min,
        Halftone::DitherType ditherType, bool color)
{
        const color_space kColorSpace = B_RGB32;
        const float right = Bounds().Width();
        const float bottom = Bounds().Height();
        BRect rect(0, 0, right, bottom);

        BBitmap testImage(rect, kColorSpace, true);
        BBitmap preview(rect, kColorSpace);
        BView view(rect, "", B_FOLLOW_ALL, B_WILL_DRAW);

        // create test image
        testImage.Lock();
        testImage.AddChild(&view);

        // color bars
        const int height = Bounds().IntegerHeight()+1;
        const int width  = Bounds().IntegerWidth()+1;
        const int delta  = height / 4;
        const float red_bottom   = delta - 1;
        const float green_bottom = red_bottom + delta;
        const float blue_bottom  = green_bottom + delta;
        const float gray_bottom  = height - 1;

        for (int x = 0; x <= right; x ++) {
                uchar value = x * 255 / width;

                BPoint from(x, 0);
                BPoint to(x, red_bottom);
                // red
                view.SetHighColor(255, value, value);
                view.StrokeLine(from, to);
                // green
                from.y = to.y+1;
                to.y = green_bottom;
                view.SetHighColor(value, 255, value);
                view.StrokeLine(from, to);
                // blue
                from.y = to.y+1;
                to.y = blue_bottom;
                view.SetHighColor(value, value, 255);
                view.StrokeLine(from, to);
                // gray
                from.y = to.y+1;
                to.y = gray_bottom;
                view.SetHighColor(value, value, value);
                view.StrokeLine(from, to);
        }

        view.Sync();
        testImage.RemoveChild(&view);
        testImage.Unlock();

        // create preview image
        Halftone halftone(kColorSpace, gamma, min, ditherType);
        halftone.SetBlackValue(Halftone::kLowValueMeansBlack);

        const int widthBytes = (width + 7) / 8; // byte boundary
        uchar* buffer = new uchar[widthBytes];

        const uchar* src = (uchar*)testImage.Bits();
        uchar* dstRow = (uchar*)preview.Bits();

        const int numPlanes = color ? 3 : 1;
        if (color) {
                halftone.SetPlanes(Halftone::kPlaneRGB1);
        }

        for (int y = 0; y < height; y ++) {
                for (int plane = 0; plane < numPlanes;  plane ++) {
                        // halftone the preview image
                        halftone.Dither(buffer, src, 0, y, width);

                        // convert the plane(s) to RGB32
                        ColorRGB32Little* dst = (ColorRGB32Little*)dstRow;
                        const uchar* bitmap = buffer;
                        for (int x = 0; x < width; x ++, dst ++) {
                                const int bit = 7 - (x % 8);
                                const bool isSet = (*bitmap & (1 << bit)) != 0;
                                uchar value = isSet ? 255 : 0;

                                if (color) {
                                        switch (plane) {
                                                case 0: dst->red = value;
                                                        break;
                                                case 1: dst->green = value;
                                                        break;
                                                case 2: dst->blue = value;
                                                        break;
                                        }
                                } else {
                                        dst->red = dst->green = dst->blue = value;
                                }

                                if (bit == 0) {
                                        bitmap ++;
                                }
                        }
                }

                // next row
                src += testImage.BytesPerRow();
                dstRow += preview.BytesPerRow();
        }

        delete[] buffer;

        SetViewBitmap(&preview);
        Invalidate();
}


HalftoneView::HalftoneView(BRect frame, const char* name, uint32 resizeMask,
        uint32 flags)
        :
        BView(frame, name, resizeMask, flags)
{
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);

        BRect r(frame);
        float size, max;

        r.OffsetTo(0, 0);
        const int height = r.IntegerHeight()+1;
        const int delta  = height / 4;
        const float red_top   = 0;
        const float green_top = delta;
        const float blue_top  = green_top + delta;
        const float gray_top  = r.bottom - delta;

        const char* kRedLabel   = "Red: ";
        const char* kGreenLabel = "Green: ";
        const char* kBlueLabel  = "Blue: ";
        const char* kGrayLabel  = "Black: ";

        BFont font(be_plain_font);
        font_height fh;
        font.GetHeight(&fh);

        max = size = font.StringWidth(kRedLabel);
        r.Set(0, 0, size, fh.ascent + fh.descent);
        r.OffsetTo(0, red_top);
        r.right = r.left + size;
        AddChild(new BStringView(r, "red", kRedLabel));

        size = font.StringWidth(kGreenLabel);
        r.Set(0, 0, size, fh.ascent + fh.descent);
        if (max < size) max = size;
        r.OffsetTo(0, green_top);
        r.right = r.left + size;
        AddChild(new BStringView(r, "green", kGreenLabel));

        size = font.StringWidth(kBlueLabel);
        r.Set(0, 0, size, fh.ascent + fh.descent);
        if (max < size) max = size;
        r.OffsetTo(0, blue_top);
        r.right = r.left + size;
        AddChild(new BStringView(r, "blue", kBlueLabel));

        size = font.StringWidth(kGrayLabel);
        r.Set(0, 0, size, fh.ascent + fh.descent);
        if (max < size) max = size;
        r.OffsetTo(0, gray_top);
        r.right = r.left + size;
        AddChild(new BStringView(r, "gray", kGrayLabel));

        r = frame;
        r.OffsetTo(max, 0);
        r.right -= max;
        fPreview = new HalftonePreviewView(r, "preview", resizeMask, flags);
        AddChild(fPreview);
}


void
HalftoneView::Preview(float gamma, float min,
        Halftone::DitherType ditherType, bool color)
{
        fPreview->Preview(gamma, min, ditherType, color);
}