/* CLIP_A */ /* CLIP 1.04 C++ Library for Image Processing Version 1.04 May 2005 Filename: "picture.h" John A Robinson http://www.intuac.com/userport/john/clip104.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version. This program 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 Licence for more details. Portions of this software are copyright as follows: Extracts from the CImg class library Copyright David Tschumperlé GNU General Public Licence The Small JPEG decoder library Copyright Rich Geldreich, 1994 - 2000 GNU Lesser General Public Licence Inverse DCT functions from The Independent JPEG Group Copyright Tom Lane, 1991 - 1998 Bespoke licence Extracts from OpenIllusionist Copyright Justen Hyde and Dan Parnham, 2004. GNU General Public Licence CameraFramework class Copyright Enrico Costanza, 2003. GNU General Public Licence Adaptive Prediction Trees encoding and decoding Copyright John Robinson 1995 - 2004 Bespoke licence Extracts from the Numerics library Copyright Brent Worden, 1998 - 1999. Bespoke licence (unrestricted use) Extracts from the Bioinformatics Template Library Copyright Birkbeck College, 1997 - 1998. GNU General Public Licence All other code Copyright John Robinson 1994 - 2005 GNU General Public Licence The bespoke licences referred to above all allow free use and adaptation of code, but some (e.g. the GNU Lesser Public Licence and the APT licence) allow use in non-free packages, whereas the GNU General Public Licence requires that any adapted versions are also free and available in source form. This program is released under the General Public Licence because it uses other code that was released under that licence. Contents: Major section Starting tag (search for this to find the start of the section) -------------------------------------------------------------- Commentary header CLIP_A Header for doxygen CLIP_B Start of code CLIP_C Declarations of the CLIP types CLIP_D This documents the API and has doxygen-formatted commentary. It is equivalent to the old CLIP header file when the rest of the package was a library of separate source files. Image display CLIP_E Camera input CLIP_F JPEG decoding CLIP_G APT encoding/decoding CLIP_H Generic image file input/output CLIP_I Font and text picture constructors CLIP_J CLIP core functions CLIP_K CLIP matrix functions CLIP_L */ /* CLIP_B */ /// \file picture.h /// /// CLIP is a C++ class library for manipulating pictures with integer /// pel values. It has a simple programming model, efficient support for /// common image processing operations, Matlab-like constructs for /// subimages, real-time capture and display. It works on MS Windows, /// Linux and Macintosh platforms. /// /// CLIP is distributed as a single header file that incorporates all /// functionality. I recommend that you use precompilation of the /// header if your compiler supports it. /* CLIP_C */ #ifndef _CLIP_1_0_1 #define _CLIP_1_0_1 #include #include #include #include #include #include #include #include #include #include #include #include #include // Put the following inside the clip scope if you don't like std being // included in global. using namespace std; /* CLIP_D */ // Begin effective header file: All CLIP types are declared in this section. // CLIP classes are in the clip namespace. But if you look at the end of // this file, you'll see the directive: using namespace clip; . This might // be evil, but it means that old CLIP programs work without change. // Start of CLIP scope namespace clip { // Have to declare things from clip's internals that are used in the // type declarations. namespace clip_internal { struct CImgDisplay; template class double_dereferenced_picture_of_; } // And the classes everyone is going to be friends with: template class picture_of_; template class colour_picture_of_; /// Represents a range of integers with constant spacing, e.g. 2, 5, 8, .., 32 /// iranges are used chiefly to index picture_of_ints and colour_picture_of_ints /// to define subimages. class irange { int start, end, spacing; int num_in_irange; public: /// irange r(0,1,7); means r represents the range of ints /// 0,1,2,3,4,5,6,7. Similarly, irange s(3,7,31); means /// s represents 3,7,11,15,19,23,27,31. irange(const int& first, const int& second, const int& last); /// irange r(5); means that the range is just the single integer 5. irange(const int& solevalue); /// Conventional copy constructor irange(const irange& other); /// Conventional assignment. irange& operator= (const irange& other); /// Instantiates range containing just the integer 0 irange(); /// Destructor ~irange(); /// Returns number of integers in the range int length(); /// Example: If we declared irange r(0,2,10); then the numbers in /// the range are 0,2,4,6,8,10 and r[3] equals 6. int operator[](const int& i) const; /// Test for ranges identical int operator== (const irange& other) const; /// Test for ranges different int operator!= (const irange& other) const; /// Example: irange r(0,2,10); r+=3; Then r is 3,5,7,9,11,13. irange& operator+= (const int& i); /// Other inc and dec operators are analogous to += irange& operator-= (const int& i); irange& operator++ (); irange& operator-- (); irange operator++ (int); irange operator-- (int); // The following functions are used internally in CLIP to avoid // excessive friendship (which VC6 doesn't seem to like). // They are probably not useful in applications. int length() const { return num_in_irange; } int istart() const { return start; } int iend() const { return end; } int ispacing() const { return spacing; } }; // // Declarations for global functions that use iranges // irange operator+(const irange& o, const int& i); irange operator+(const int& i, const irange& o); irange operator-(const irange& o, const int& i); irange operator-(const int& i, const irange& o); //-------------------------------------- // picture_of_int and ancilliary classes //-------------------------------------- // The pic_rep class is used internally by CLIP namespace clip_internal { template class pic_rep{ PEL **buf; int border; int rows, cols; int numrefs; pic_rep(const int& r, const int& c, const int& bord) { int i; // Counter rows = r; cols = c; border = bord; buf = new PEL *[rows+2*border]; buf[0] = new PEL[(rows+2*border)*(cols+2*border)]; buf[0] += border; for (i = 1; i < rows+2*border; i++) { buf[i] = buf[0] + i*(cols+2*border); } buf += border; // Now set the border to zero for (i = -border; i < 0; i++) memset(buf[i]-border,0,(cols+2*border)*sizeof(PEL)); for (; i < rows; i++) { memset(buf[i]-border,0,border*sizeof(PEL)); memset(buf[i]+cols,0,border*sizeof(PEL)); } for (; i < rows+border; i++) memset(buf[i]-border,0,(cols+2*border)*sizeof(PEL)); } template // See below for case when OTHERPEL is same type as PEL pic_rep(const pic_rep& other) { // cerr << "pic rep copy constr\n"; rows = other.rows; cols = other.cols; border = other.border; buf = new PEL *[rows+2*border]; buf[0] = new PEL[(rows+2*border)*(cols+2*border)]; buf[0] += border; int i; for (i = 1; i < rows+2*border; i++) { buf[i] = buf[0] + i*(cols+2*border); } buf += border; // Now copy data over for (i = -border; i < rows+border; i++) for (int j = -border; j < cols+border; j++) buf[i][j] = (PEL)other.buf[i][j]; } // template <> pic_rep(const pic_rep& other) { // cerr << "pic rep copy constr\n"; rows = other.rows; cols = other.cols; border = other.border; buf = new PEL *[rows+2*border]; buf[0] = new PEL[(rows+2*border)*(cols+2*border)]; buf[0] += border; for (int i = 1; i < rows+2*border; i++) { buf[i] = buf[0] + i*(cols+2*border); } buf += border; // Now copy data over memcpy(&buf[-border][-border],&other.buf[-border][-border], (rows+2*border)*(cols+2*border)*sizeof(PEL)); } void dc_pad() { int i, j; for (i = 0; i < rows; i++) { for (j = -border; j < 0; j++) buf[i][j] = buf[i][0]; for (j = cols; j < cols+border; j++) buf[i][j] = buf[i][cols-1]; } for (i = -border; i < 0; i++) memcpy(buf[i]-border,buf[0]-border,(cols+2*border)*sizeof(PEL)); for (i = rows; i < rows+border; i++) memcpy(buf[i]-border,buf[rows-1]-border, (cols+2*border)*sizeof(PEL)); } void zero_border() { int i; for (i = 0; i < rows; i++) { memset(buf[i]-border,0,border*sizeof(PEL)); memset(buf[i]+cols,0,border*sizeof(PEL)); } for (i = -border; i < 0; i++) memset(buf[i]-border,0,(cols+2*border)*sizeof(PEL)); for (i = rows; i < rows+border; i++) memset(buf[i]-border,0, (cols+2*border)*sizeof(PEL)); } ~pic_rep() { delete [] (buf[-border]-border); buf -= border; delete [] buf; } // Have to deal with friends differently because of MSC VC++ problem #if ( defined(_MSC_VER)) // Instead have to do friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class double_dereferenced_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; friend class pic_rep; #else template friend class picture_of_; template friend class double_dereferenced_picture_of_; template friend class colour_picture_of_; template friend class pic_rep; #endif }; } const int DEFAULT_BORDER = 32; // When a picture is instantiated // without an explicit border size, // this is the size used. (See the // picture_of_int constructors below). /// The main class in CLIP. A picture_of_ is a one-plane (greylevel) image. /// picture_of_s are the basic type in CLIP: colour_picture_of_s are made out /// of them and for greylevel processing, they are all you need. template class picture_of_ { // Private part of picture_of_int // Following data items are used for sequence processing: bool seq; int numfiles; int currfile; bool usingcam; int firstcycle; char **av; picture_of_ *backup; // Have to deal with friends differently because of MSC VC++ problem #if (defined(_MSC_VER)) friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class clip_internal::double_dereferenced_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; #else template friend class picture_of_; template friend class clip_internal::double_dereferenced_picture_of_; template friend class colour_picture_of_; #endif protected: clip_internal::pic_rep *pr; struct clip_internal::CImgDisplay *qxp; irange rowrange, colrange; int dereferenced; // 1 = singly, 2 = doubly // Function for averaging three channels into one (colour to monochrome) template picture_of_& average_assign(const picture_of_& rb, const picture_of_& rc, const picture_of_& rd) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: range["<buf[outr],rb.pr->buf[inr], rc.pr->buf[inr],rd.pr->buf[inr],colrange,rb.colrange); outr += rowrange.ispacing(); inr += rb.rowrange.ispacing(); } return *this; } // Templated for dealing with all kinds of update between all kinds of // picture_of objects template picture_of_& assign(const picture_of_& rb, const char& sel) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: range["<buf[outr],rb.pr->buf[inr], colrange, rb.colrange); outr += rowrange.ispacing(); inr += rb.rowrange.ispacing(); } return *this; } // Non-templated for dealing with assignment of same kind of picture picture_of_& assign(const picture_of_& rb) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: range["<buf[outr],rb.pr->buf[inr], colrange, rb.colrange); outr += rowrange.ispacing(); inr += rb.rowrange.ispacing(); } return *this; } picture_of_& assign(const PEL& newval, const char& sel) { // Fill templine, then do other lines by copying PEL *temp = new PEL[colrange.length()]; for (int j = 0; j < colrange.length(); j++) temp[j] = newval; irange temprange(0,1,colrange.length()-1); for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { if ((sel == '=')&&(colrange.ispacing() == 1)) // Do fast memcpy(&pr->buf[i][colrange.istart()],temp, colrange.length()*sizeof(PEL)); else row_update_op(sel, pr->buf[i], temp, colrange, temprange); } delete [] temp; return *this; } PEL sum(const picture_of_& rb, const char& sel) const { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: picture["<buf[outr],rb.pr->buf[inr], colrange, rb.colrange); outr += rowrange.ispacing(); inr += rb.rowrange.ispacing(); } return sum; } void row_mask_op(const int& thresh, const int& below, const int& equal, const int& above, register PEL *dest, register PEL *source, const irange& destrange, const irange& sourcerange){ register int i = destrange.length(); if (sourcerange.length() < i) i = sourcerange.length(); dest += destrange.istart(); source += sourcerange.istart(); register int dinc = destrange.ispacing(); register int sinc = sourcerange.ispacing(); register PEL current; for (;i--;dest+=dinc,source+=sinc) { current = *source; if (current > thresh) *dest = above; else if (current == thresh) *dest = equal; else *dest = below; } } void row_limits_op(const char& sel, const register PEL *source, const irange& sourcerange, PEL& currlim) const { register int i = sourcerange.length(); source += sourcerange.istart(); register int sinc = sourcerange.ispacing(); register PEL lim = currlim; switch (sel) { case 'n': // Find minimum for (; i--; source+=sinc) { if (*source < lim) lim = *source; } break; case 'x': // Find maximum for (; i--; source+=sinc) { if (*source > lim) lim = *source; } break; } currlim = lim; } void row_limits_op(const char& sel, register PEL *source, const irange& sourcerange, const PEL& newlim) { register int i = sourcerange.length(); source += sourcerange.istart(); register int sinc = sourcerange.ispacing(); switch (sel) { case 'N': // Set minimum (clip) for (; i--; source+=sinc) { if (*source < newlim) *source = newlim; } break; case 'X': // Set maximum (clip) for (; i--; source+=sinc) { if (*source > newlim) *source = newlim; } break; } } void row_assign_op(register PEL *dest, register PEL *source, const irange& destrange, const irange& sourcerange) { register int i = destrange.length(); if (sourcerange.length() < i) i = sourcerange.length(); dest += destrange.istart(); source += sourcerange.istart(); register int dinc = destrange.ispacing(); register int sinc = sourcerange.ispacing(); if ((sinc==1)&&(dinc==1)) memcpy(dest,source,i*sizeof(PEL)); else { for (;i--;dest+=dinc,source+=sinc) *dest = *source; } } template void row_average_op( register PEL *dest, register OTHERPEL *s1, register OTHERPEL *s2, register OTHERPEL *s3, const irange& destrange, const irange& sourcerange) { register int i = destrange.length(); if (sourcerange.length() < i) i = sourcerange.length(); dest += destrange.istart(); s1 += sourcerange.istart(); s2 += sourcerange.istart(); s3 += sourcerange.istart(); register int dinc = destrange.ispacing(); register int sinc = sourcerange.ispacing(); for (;i--;dest+=dinc,s1+=sinc,s2+=sinc,s3+=sinc) *dest = (PEL)((*s1+*s2+*s3)/3); } template void row_update_op(const char& sel, register PEL *dest, register OTHERPEL *source, const irange& destrange, const irange& sourcerange) { register int i = destrange.length(); if (sourcerange.length() < i) i = sourcerange.length(); dest += destrange.istart(); source += sourcerange.istart(); register int dinc = destrange.ispacing(); register int sinc = sourcerange.ispacing(); switch (sel) { case '=': for (;i--;dest+=dinc,source+=sinc) *dest = (PEL) *source; break; case '+': for (;i--;dest+=dinc,source+=sinc) *dest += (PEL) *source; break; case '-': for (;i--;dest+=dinc,source+=sinc) *dest -= (PEL) *source; break; case '*': for (;i--;dest+=dinc,source+=sinc) *dest *= (PEL) *source; break; case '/': for (;i--;dest+=dinc,source+=sinc) { if (*source == 0) { cout << "Attempt to divide by a pel "; cout << "with value 0. Dividing by "; cout << "1 instead." << endl; } else *dest /= (PEL) *source; } break; case '|': // Absolute difference for (;i--;dest+=dinc,source+=sinc) { register PEL tempd = *dest; register PEL temps = (PEL) *source; if (tempd > temps) *dest = tempd - temps; else *dest = temps - tempd; } break; case '^': // Squared difference for (;i--;dest+=dinc,source+=sinc) { register PEL temp = *dest - (PEL) *source; *dest = temp*temp; } break; case '&': // Conditional replacement for (;i--;dest+=dinc,source+=sinc) { register PEL zero = 0; // Above line to avoid compiler warnings // when comparing unsigned types. register PEL temp = (PEL) *source; if (temp >= zero) *dest = temp; } break; default: for (;i--;dest+=dinc,source+=sinc) *dest = (PEL) *source; break; } } PEL row_sum_op(const char& sel, register PEL *first, register PEL *second, const irange& firstrange, const irange& secondrange) const { register int i = firstrange.length(); if (secondrange.length() < i) i = secondrange.length(); first += firstrange.istart(); second += secondrange.istart(); register int finc = firstrange.ispacing(); register int sinc = secondrange.ispacing(); PEL sum = 0; switch (sel) { case '=': for (;i--;first+=finc,second+=sinc) sum += *second; break; case '+': for (;i--;first+=finc,second+=sinc) sum += *first + *second; break; case '-': for (;i--;first+=finc,second+=sinc) sum += *first - *second; break; case '*': for (;i--;first+=finc,second+=sinc) sum += *first * *second; break; case '/': for (;i--;first+=finc,second+=sinc) { if (*second == 0) { cout << "Attempt to divide by a pel "; cout << "with value 0. Dividing by "; cout << "1 instead." << endl; sum += *first; } else sum += *first / *second; } break; case '|': // Absolute difference for (;i--;first+=finc,second+=sinc) { register PEL temp = *first - *second; if (temp < 0) temp = -temp; sum += temp; } break; case '^': // Squared difference for (;i--;first+=finc,second+=sinc) { register PEL temp = *first - *second; sum += temp*temp; } break; default: for (;i--;first+=finc,second+=sinc) sum += *second; break; } return sum; } void init_new(const int& r, const int& c, const int& bord) { using clip_internal::pic_rep; irange temprow(0,1,r-1); irange tempcol(0,1,c-1); rowrange = temprow; colrange = tempcol; dereferenced = 0; qxp = 0; // Initialized off pr = new pic_rep(r, c, bord); } void fake_new(const picture_of_& other) { // Used only for setting up colour pictures that are pure monochrome const int r = other.nrows(); const int c = other.ncols(); irange temprow(0,1,r-1); irange tempcol(0,1,c-1); rowrange = temprow; colrange = tempcol; dereferenced = 0; qxp = 0; // Initialized off pr = other.pr; } // Following constructor sets up dereferenced version picture_of_(const irange& range, const picture_of_& source) // The only contructor that doesn't use init_new() { pr = source.pr; // Note: no copying of data qxp = 0; // Initialized off if (source.dereferenced == 0) { dereferenced = 1; rowrange = range; colrange = source.colrange; } else if (source.dereferenced == 1) { dereferenced = 2; rowrange = source.rowrange; colrange = range; } } // And an equivalent function called from colour_picture_of_ void dereffrom(const irange& range, const picture_of_& source) // Exact copy of the above constructor // Called only after null constructor in colour_picture_of_ { pr = source.pr; // Note: no copying of data qxp = 0; // Initialized off if (source.dereferenced == 0) { dereferenced = 1; rowrange = range; colrange = source.colrange; } else if (source.dereferenced == 1) { dereferenced = 2; rowrange = source.rowrange; colrange = range; } } // // Following constructor and updater are only accessed by the // friend class colour_picture_of_ // picture_of_(void) { // Does nothing } void initialize(const int r, const int c, const int bord = DEFAULT_BORDER) { init_new(r,c,bord); for (int i = 0; i < r; i++) memset(pr->buf[i],0,c*sizeof(PEL)); } void fake_initialize(const picture_of_& other) { // Called from a colour picture that is pure monochrome fake_new(other); } int construct_or_read_from_file(const char *pnmname, const int bord = DEFAULT_BORDER, const int readflag = 1); // Now follows the part accessible by applications public: /// Instantiates a picture by reading it in from a file. E.g. /// picture_of_int in("goldhill256"); sets up in and reads in goldhill256. /// The following file formats are read: PGM, PPM, JPEG, APT and the most /// common BMP formats. /// If the file is in colour, it is read and converted to monochrome. /// The bord argument specifies how many pels border to add around the /// picture. You don't have to allow for the border when indexing the /// picture. Having a border allows you to do, e.g., in[-1][-1] /// without causing a segmentation fault. This is useful when writing /// local operators that slide over the picture: You don't have to treat /// the edges specially. picture_of_(const char *pnmname, const int bord=DEFAULT_BORDER) { if (construct_or_read_from_file(pnmname, bord, 0) >= 0) return; qxp = 0; char errmsg[180]; sprintf(errmsg,"CLIP cannot read\n%s\nas a picture",pnmname); picture_of_ temp(errmsg,0,255); int r=temp.nrows(); int c=temp.ncols(); init_new(r,c,bord); for (int i = 0; i < r; i++) for (int j = 0; j < c; j++) pr->buf[i][j] = temp[i][j]; } /// Instantiates a zeroed pic of given dimensions. E.g. /// picture_of_int pic(240,320); creates a zeroed (black) picture /// of size 320 columns by 240 rows. picture_of_(const int r, const int c, const int bord=DEFAULT_BORDER) { init_new(r,c,bord); for (int i = 0; i < r; i++) memset(pr->buf[i],0,c*sizeof(PEL)); } /// Creates a picture from a 1D array of the pels in scan order. Rarely used. picture_of_(const int r, const int c, const PEL *in, const int bord=DEFAULT_BORDER) { init_new(r,c,bord); for (int i = 0; i < r; i++) { memcpy(pr->buf[i],in,c*sizeof(PEL)); in += c; } } /// Conventional copy constructor template picture_of_(const picture_of_& other) { using clip_internal::pic_rep; dereferenced = other.dereferenced; rowrange = other.rowrange; colrange = other.colrange; qxp = 0; // Initialized off if (dereferenced) // Copy constructor does not copy pic_rep pr = (pic_rep *) other.pr; // Above cast ok because never dereference to another type. else pr = new pic_rep(*other.pr); } // And the special case of constructing from same type that VC doesn't // seem to handle as part of above constructor. // template <> picture_of_(const picture_of_& other) { using clip_internal::pic_rep; dereferenced = other.dereferenced; rowrange = other.rowrange; colrange = other.colrange; qxp = 0; // Initialized off if (dereferenced) // Copy constructor does not copy pic_rep pr = other.pr; // Above cast ok because never dereference to another type. else pr = new pic_rep(*other.pr); } /// Create from a colour_picture_of_int (includes conversion to monochrome) template picture_of_(const colour_picture_of_& other) { using clip_internal::pic_rep; qxp = 0; // Initialized off int colismono = other.ismonochrome(); if ((colismono && other.mono.dereferenced)|| (!colismono && other.red.dereferenced)) { cerr << "Sorry. You cannot construct a picture_of_ "; cerr << "from an\nirange-dereferenced colour_picture_of_. "; cerr << "Use assignment.\n"; exit(1); } dereferenced = 0; if (colismono) { rowrange = other.mono.rowrange; colrange = other.mono.colrange; pr = new pic_rep(*other.mono.pr); } else { rowrange = other.red.rowrange; colrange = other.red.colrange; int r = rowrange.length(); int c = colrange.length(); int bord = other.red.pr->border; init_new(r,c,bord); average_assign(other.green,other.red,other.blue); } } /// Modify-source constructor that applies callback point function (*f) /// over source to create new picture. picture_of_(const picture_of_& source, PEL (*f)(PEL&, const PEL&)) { int r = source.rowrange.length(); int c = source.colrange.length(); int bord = source.pr->border; init_new(r, c, bord); PEL *p; int outrow = 0; for (int i = source.rowrange.istart(); i <= source.rowrange.iend(); i += source.rowrange.ispacing()) { p = pr->buf[outrow++]; for (int j = source.colrange.istart(); j <= source.colrange.iend(); j += source.colrange.ispacing()) f(*p++, source.pr->buf[i][j]); } } /// Modify-source constructor that applies callback neighbourhood function (*f) /// over source to create new picture. picture_of_(const picture_of_& source, int (*f)(PEL&, const PEL **)) { int i,j; // Counters int r = source.rowrange.length(); int c = source.colrange.length(); int bord = source.pr->border; init_new(r, c, bord); // Have to reset pr row pointers so f works as expected int totalrows = source.pr->rows; int sourcecoloff = source.colrange.istart(); for (i = 0-bord; i < totalrows+bord; i++) source.pr->buf[i] += sourcecoloff; for (j = 0; j < c; j++) { int sourcerowoff = source.rowrange.istart(); for (i=0; ibuf[i][j], (const PEL **) &source.pr->buf[sourcerowoff]); // Throw away the return val because this is a constructor for (i = 0-bord; i < totalrows+bord; i++) source.pr->buf[i] += source.colrange.ispacing(); sourcecoloff += source.colrange.ispacing(); } for (i = 0-bord; i < totalrows+bord; i++) source.pr->buf[i] -= sourcecoloff; } /// The ONLY way to create text (e.g. labels) in CLIP is to instantiate /// a picture_of_int with the appropriate text. This creates a picture with /// lettering level fg on a ground level bg. size is an integer scale. /// The result can be combined into your pictures as required. picture_of_(const char *text, const int fg, const int bg, const int size=1); /// Read picture from file into object. The picture_of_int object is NOT /// resized, so read only reads as much of the input file as will fit. int read(const char *name); /// Write picture to a PGM file. PGM is CLIP's "native" file format. int write(const char *pgmname); /// Write picture in APT format. 0 <= quality <= 100. 100=lossless. int aptwrite(const char *aptname, const int quality = 100); /// Write picture in BMP format. If you must. int bmpwrite(const char *bmpname); /// Load from a colour_picture_of_, stacking into a colour format that can /// be used by functions like showascolour(). int loadascolour(const colour_picture_of_& other); /// Destructor ~picture_of_() { // cerr << "picture_of_ delete\n"; if (dereferenced) return; if (qxp) delete qxp; delete pr; } /// Set values in the border of the picture by copying the values on /// the edges of the picture outwards. void dc_pad() { pr->dc_pad(); } /// Set values in the border of the picture to zero. void zero_border() { pr->zero_border(); } /// Reflects picture at boundaries to prevent edge effects for a filter of /// horizontal length hlength and vertical length vlength. /// When convolving an image whose left hand side is /// a b c d e f ... /// with a filter with an odd number of taps, the picture should be /// 'reflected' in the boundary as: /// ... f e d c b | a b c d e f ... /// where | represents the boundary. /// Convolving with a filter with an even number of taps, requires reflecting: /// ... f e d c b a | a b c d e f ... /// symmetric_extension does the appropriate reflection based on given lengths. void symmetric_extension(int hlength, int vlength) { int heven = ((hlength>>1)<<1 == hlength); // = 1 if even num of taps int veven = ((vlength>>1)<<1 == vlength); // = 1 if even num of taps for (int i = 0; i < nrows(); i++) { for (int j = -hlength/2; j < 0; j++) { pr->buf[i][j] = pr->buf[i][-j-heven]; pr->buf[i][ncols()-1-j] = pr->buf[i][ncols()-1+j+heven]; } } for (int j = 0; j < ncols(); j++) { for (int i = -vlength/2; i < 0; i++) { pr->buf[i][j] = pr->buf[-i-veven][j]; pr->buf[nrows()-1-i][j] = pr->buf[nrows()-1+i+veven][j]; } } } /// Return number of rows in picture int nrows() const {return rowrange.length();} /// Return number of columns in picture int ncols() const {return colrange.length();} /// Return width of border int bordersize() const {return pr->border; } /// Return maximum value in picture and write its position into /// reti (row) and retj (column). PEL max(int& reti, int& retj) const { PEL retval = pr->buf[0][0]; for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) if (pr->buf[i][j] > retval) { retval = pr->buf[i][j]; reti = i; retj = j; } } return retval; } /// Return minimum value in picture and write its position into /// reti (row) and retj (column). PEL min(int& reti, int& retj) const { PEL retval = pr->buf[0][0]; for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) if (pr->buf[i][j] < retval) { retval = pr->buf[i][j]; reti = i; retj = j; } } return retval; } /// Return maximum value in picture PEL max() const { PEL retval = pr->buf[0][0]; for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) row_limits_op('x',pr->buf[i],colrange,retval); return retval; } /// Return minimum value in picture PEL min() const { PEL retval = pr->buf[0][0]; for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) row_limits_op('n',pr->buf[i],colrange,retval); return retval; } /// Clip picture at given maximum value void max(const PEL clipval) { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) row_limits_op('X',pr->buf[i],colrange,clipval); } /// Clip picture at given minimum value void min(const PEL clipval) { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) row_limits_op('N',pr->buf[i],colrange,clipval); } /// Return total of all pel values PEL total() const { PEL retval = 0; for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) retval += pr->buf[i][j]; } return retval; } /// \name Sequence processing facilities /// Sequence processing facilities for "standard" CLIP programs. /// Many CLIP programs consist of a processing loop that cycles over /// input pictures from a sequence of named files or from a camera. //@{ /// Sequence processing constructor that interprets main()'s parameters. /// If the command line had no arguments, tries to attach a camera. /// If that fails, generates a default picture. /// If the command line had arguments, interprets these as picture /// files and sets up for reading these in sequence. /// See also member function next() picture_of_(int argc, char *argv[], const int def_nrows=240, const int def_ncols=320, const int bord=DEFAULT_BORDER) { seq = 1; // Flag to say sequence processing numfiles = argc - 1; currfile = 1; usingcam = 0; firstcycle = 1; av = argv; qxp = 0; backup = 0; if (!numfiles) { // If there are no arguments, try to open a camera and, if // that fails, create a default image initialize(def_nrows, def_ncols, bord); if (attach_camera() < 0) { hramp(); clip::picture_of_ text("Default image",0,128,3); paste_centred_on(text, def_ncols/2,def_nrows/2); } else { request_frame(); usingcam = 1; } return; } // Otherwise, open the first file if (construct_or_read_from_file(argv[1], bord, 0) >= 0) { seq = 1; if (numfiles == 1) // Make and save a backup for fast reread backup = new picture_of_(*this); return; } qxp = 0; char errmsg[180]; sprintf(errmsg,"CLIP cannot read\n%s\nas a picture",argv[1]); clip::picture_of_ temp(errmsg,0,255); int r=temp.nrows(); int c=temp.ncols(); initialize(r, c, bord); *this = temp; if (numfiles == 1) // Make and save a backup for fast reread backup = new picture_of_(*this); } /// Loads the next picture in the sequence into this picture that was /// constructed with the sequence processing constructor. Returns 1 if /// the picture has been seen before (if cycling back to the start of /// the argument list) and 0 if it has not. Returns -1 if the picture /// was not constructed using the sequence processing constructor. int next() { if (!seq) // Not set up to do sequence processing { cerr << "next() called for a picture not constructed for "; cerr << "sequence processing." << endl; return -1; } if (usingcam) { while(!frame_delivered()) ; request_frame(); return 1; // Not seen this picture before } if (!numfiles){ // Using default picture // Remake it, just in case it was overwritten hramp(); clip::picture_of_ text("Default image",0,128,3); paste_centred_on(text,ncols()/2,nrows()/2); return 0; } if (numfiles == 1){ // We made a backup of this one to save // rereading every cycle *this = *backup; return 0; } if (currfile > numfiles) { currfile = 1; // Back to start firstcycle = 0; } read(av[currfile++]); return firstcycle; } //@} /// Reranges or normalizes picture values to fall in given range /// If scale_even_if_inside_range is false (default), then if all /// values are current between the give newlow and newhigh, they /// are unaltered. Otherwise, values are scaled so that the new /// max() will be newhigh and the new min() will be newmin. int rerange(const PEL newlow, const PEL newhigh, const bool scale_even_if_inside_range = 0) { if (newlow == newhigh) { *this = newhigh; return 0; } PEL mx = max(); PEL mn = min(); if ((mx == newhigh) && (mn == newlow)) return 0; if (!scale_even_if_inside_range && (mx <= newhigh) && (mn >= newlow)) return 0; if (mx == mn) { if (mx > 0) *this = newhigh; else *this = newlow; return 0; } PEL newrange = newhigh - newlow; PEL oldrange = mx - mn; for (int i = 0; i < nrows(); i++) { PEL *pel = pr->buf[i]; for (int j = 0; j < ncols(); j++) { *pel = newlow + (newrange*(*pel-mn))/oldrange; pel++; } } return 1; } /// \name Picture display functions /// Use these functions to show or reshow the current picture on the screen //@{ /// Display the picture on the screen in its own window, with /// the comment string as title. If normalize is true values are scaled /// to [0,255] for display. If false, it is assumed they already are in this range. int show(const char *comment, const bool normalize=1); /// Display picture without a specific title. int show(const bool n = 1) { return show("CLIP image",n); } /// Display without decorations at Nx,Ny. If parent==0, the coords are /// screen coordinates, and the window receives events (e.g. pointx()) /// as normal. If parent is a displayed picture, the coords are relative to /// that picture and a child window is displayed that does not receive events. int showat(const int Nx, const int Ny, const bool normalize, const picture_of_ *parent); /// Display without decorations, with colour_picture_of_int as parent int showat(const int Nx, const int Ny, const bool normalize, const colour_picture_of_ *parent); /// Display without decorations at screen coords Nx, Ny int showat(const int Nx, const int Ny, const bool normalize) { return showat(Nx,Ny,normalize,(const picture_of_ *)0); } /// Display without decorations at screen coords Nx, Ny int showat(const int Nx, const int Ny) { return showat(Nx,Ny,1,(const picture_of_ *)0); } /// Redisplay the picture (presumably because it has changed) and change /// title to the comment string. int reshow(const char *comment, const bool normalize=1); /// Redisplay the picture without altering the title. int reshow(const bool n = 1); /// Change title of the displayed pic. int rename(const char *comment); /// Stop showing the picture int unshow(); /// Display a picture_of_int as if each integer pel were a /// colour vector of four unsigned chars in the order alpha (most /// significant) - red - green - blue. Use comment string as window title. int showascolour(const char *comment); /// As above, without window title int showascolour() { return showascolour("CLIP image"); } /// Redisplay a picture_of_int as if each integer pel were a colour /// vector of four unsigned chars in the order alpha-red-green-blue. /// Change title to comment string. int reshowascolour (const char *comment); /// As above, without change in window title int reshowascolour(); /// Display a picture_of_ with showascolour() interpretation (see /// above), but without decorations at Nx,Ny. If parent==0, the coords are /// screen coordinates, and the window receives events (e.g. pointx()) /// as normal. If parent is a displayed picture, the coords are relative to /// that picture and a child window is displayed that does not receive events. /// As showascolour() but wihout decorations. int showascolourat(const int Nx, const int Ny, const picture_of_ *parent); /// As above with colour_picture_of_ parent int showascolourat(const int Nx, const int Ny, const colour_picture_of_ *parent); /// As above with no parent, i.e. at screen coordinates Nx, Ny int showascolourat(const int Nx, const int Ny) { return showascolourat(Nx,Ny,(const picture_of_ *)0); } /// Returns non-zero if the user has left-clicked in the close box of the /// window where the picture is being displayed. int closerequested(); /// Return current pointer (mouse) x coord relative to left of picture window int pointx(); /// Return current pointer (mouse) y coord relative to top of picture window int pointy(); /// Return non-zero if the mouse's left button is pressed within the picture's window. int pointleftbutton(); /// Returns non-zero if mid button down int pointmiddlebutton(); /// Returns non-zero if right button down int pointrightbutton(); /// Returns non-zero if the shift key is held down while a mouse button is down. int pointshiftbutton(); /// Returns keycode of any key held down int key(); /// Clears keystroke so that it will not be reprocessed. int clearkey(); /// Inspect a shown picture. Until the right button is clicked inside /// the picture, the program pauses and allows the user to /// left-click within the picture and inspect the values at that point. int inspect() { if (!qxp) return -1; char infostring[64]; int prevx = -1; int prevy = -1; while (pointrightbutton()); // From any previous rightbutton press while(1) { if (pointrightbutton()) { // No longer do a rename back to what was before return 1; } if (!pointleftbutton()) continue; int x = pointx(); int y = pointy(); if ((x == prevx)&&(y == prevy)) continue; prevx = x; prevy = y; if ((x < 0)||(x >= ncols())||(y < 0)||(y >= nrows())) { // Beyond picture limits rename("pointer outside pic"); continue; // Back to start of loop } // Else pointer is within picture limits float pelvalue = (float) pr->buf[y][x]; sprintf(infostring,"value at (%d,%d) = %f", x,y,pelvalue); rename(infostring); // Update header } } //@} /// \name Dereferencing operators /// The following operator[]s are used to dereference (i.e. index) pictures. /// They must always be appended to a picture_of_int in pairs, with the /// first [] specifying a row value or range and the second [] specifying /// a column value or range. E.g.: picture_of_int a(r,c); a[y][x] = 255; /// So when using []s, picture_of_int looks like a two dimensional array. /// The difference is it can be indexed by iranges and doubles as well as /// by ints. //@{ /// When dereferenced (i.e. indexed) with irange operators, a subimage or /// block of the picture is returned as a picture. E.g. irange rg(0,1,7); /// picture_of_ pic(256,256); pic[rg][rg] = 255; sets the top left 8x8 square /// in pic to white. picture_of_ operator[] (const irange& selector) const { picture_of_ m(selector,*this); return m; } /// When dereferenced by an integer, just gives entry into pel buffer. /// No bounds checking. Same as rowptr(selector); PEL * operator[] (const int& selector) { return pr->buf[selector]; } PEL * operator[] (const int& selector) const { return pr->buf[selector]; } /// When dereferenced (i.e. indexed) with a double, a PEL is returned which /// is the value bilinearly interpolated at the given double coords. E.g. /// val = a[20.5][31.435]; sets val to a weighted average of a[20][31], /// a[20][32], a[21][31] and a[21][32]. You cannot use double indexing on /// the left hand side of an expression because the pel at the given location /// doesn't really exist. I.e. saying a[20.5][31.435] = val; is illegal. /// (This works via the helper class double_dereferenced_picture_of_int /// which users do not access directly.) clip_internal::double_dereferenced_picture_of_ operator[](const double selector) { clip_internal::double_dereferenced_picture_of_ m(selector, this); return m; } /// We often want to paste one picture on another, centred at a particular /// location. Can do this with irange derefencings, but this way is easier. int paste_centred_on(const picture_of_& from, const int x, const int y) { irange r(0,1,from.nrows()-1); irange c(0,1,from.ncols()-1); r -= from.nrows()/2; c -= from.ncols()/2; r += y; c += x; operator[](r).operator[](c).assign(from,'='); return 0; } /// As above, for colour_picture_of_ pasting int paste_centred_on(const colour_picture_of_& from, const int x, const int y) { irange r(0,1,from.nrows()-1); irange c(0,1,from.ncols()-1); r -= from.nrows()/2; c -= from.ncols()/2; r += y; c += x; operator[](r).operator[](c).assign(from,'='); return 0; } //@} /// The map operators provide a fast way of mapping a bilinearly interpolated /// value from one picture into another. transparency is a number from 0 (overwrite) /// to 255 (preserve almost all of what was there originally). So with two /// picture_of_int's source and dest, dest.map(50,76,source,5.3,99.99,0); /// is exactly equivalent to dest[76][50] = source[99.99][5.3]; but much faster. int map(const int myx,const int myy, const picture_of_& from, const double fromx, const double fromy, const int transparency); /// This map operator is even faster than the double version above. This /// time fromx and fromy are both 256 times the actual values desired, so /// dest.map(50,76,source,64,384,0); is the same as /// dest.map(50,76,source,0.25,1.5,0); but faster. Rarely used. int map(const int myx,const int myy, const picture_of_& from, const int fromx,const int fromy, const int transparency); /// \name point and neighbourhood operators via callbacks //@{ /// Apply callback point function *f() over picture, modifying itself. PEL point(PEL (*f)(PEL&)) { PEL accum = 0; for (int r = rowrange.istart(); r <= rowrange.iend(); r += rowrange.ispacing()) for (int c = colrange.istart(); c <= colrange.iend(); c += colrange.ispacing()) accum += f(pr->buf[r][c]); return accum; } /// Apply callback point function *f() over source picture rb, putting /// result into *this. PEL point(const picture_of_& rb, PEL (*f)(PEL&, const PEL&)) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: picture["<buf[outr][outc], rb.pr->buf[inr][inc]); return accum; } /// Apply callback neigh function *f() over source picture rb, putting /// result into this. PEL neigh(picture_of_& rb, PEL(*f)(PEL&, const PEL **)) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: picture["<rows; int rbcoloff = rb.colrange.istart(); for (i = 0; i < totalrows; i++) rb.pr->buf[i] += rbcoloff; for (outc = colrange.istart(), inc = rb.colrange.istart(); ((outc <= colrange.iend()) && (inc <= rb.colrange.iend())); outc += colrange.ispacing(), inc += rb.colrange.ispacing()) { for (outr = rowrange.istart(), inr = rb.rowrange.istart(); ((outr <= rowrange.iend()) && (inr <= rb.rowrange.iend())); outr += rowrange.ispacing(), inr += rb.rowrange.ispacing()) accum += f(pr->buf[outr][outc], (const int **) &rb.pr->buf[inr]); for (i = 0; i < totalrows; i++) rb.pr->buf[i] += rb.colrange.ispacing(); rbcoloff += rb.colrange.ispacing(); } for (i = 0; i < totalrows; i++) rb.pr->buf[i] -= rbcoloff; return accum; } //@} /// Set pels in this picture, according to their values in source picture /// rb: if source pel is below thresh, set dest pel to value below, /// if source pel is equal to thresh, set dest pel to value equal, /// if source pel is above thresh, set dest pel to int value above. picture_of_& mask (const picture_of_& rb, const PEL& thresh,const PEL& below, const PEL& equal, const PEL& above) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: range["<buf[outr],rb.pr->buf[inr], colrange, rb.colrange); outr += rowrange.ispacing(); inr += rb.rowrange.ispacing(); } return *this; } /// Return 1 if and only if every pel in the picture equals testval. int operator==(PEL testval) { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { PEL *p = pr->buf[i]; p += colrange.istart(); int j = colrange.length(); for (; j--; p += colrange.ispacing()) if (*p != testval) return 0; } return 1; } /// \name Assignment operators //@{ /// Set picture_of_ equal to colour_picture_of_. Does conversion to monochrome template picture_of_& operator= (const colour_picture_of_& rb) { // Convert a colour_picture_of_ to monochrome if (rb.ismonochrome()) { assign(rb.mono,'='); return *this; } average_assign(rb.green,rb.red,rb.blue); return *this; } /// Set this picture equal to source picture with different data type. /// This and all assignments /// work with irange-referenced pictures. template picture_of_& operator= (const picture_of_& rb){ return assign(rb, '='); } /// Set this picture equal to source picture with same pel data type. // template <> picture_of_& operator= (const picture_of_& rb){ return assign(rb); } /// Set every pel to newval. E.g.irange rg(0,8,256-8); /// picture_of_ a(256,256); a[rg][rg] = 255; /// sets every eighth pel horizontally and vertically to white. picture_of_& operator=(PEL newval){ return assign(newval, '='); } /// Add source picture to this picture. I.e. each pel value in rb is /// added to the corresponding pel's value in this. template picture_of_& operator+= (const picture_of_& rb) { return assign(rb, '+'); } /// Add newval to every pel picture_of_& operator+= (PEL newval){ return assign(newval, '+'); } /// Subtract source picture from this picture. I.e. each pel value /// in rb is subtracted from the corresponding pel's value in this. template picture_of_& operator-= (const picture_of_& rb) { return assign(rb, '-'); } /// Subtract newval from every pel. picture_of_& operator-=(PEL newval){ return assign(newval, '-'); } /// Multiply source picture with this picture. I.e. each pel value /// in rb is multiplied with the corresponding pel's value in this. template picture_of_& operator*=(const picture_of_& rb) { return assign(rb, '*'); } /// Multiply every pel by newval picture_of_& operator*=(PEL newval){ return assign(newval, '*'); } /// If a pel in rb is 0, divides the corresponding pel in this /// by 1, otherwise divides it by rb pel value. template picture_of_& operator/=(const picture_of_& rb) { return assign(rb, '/'); } /// Divide every pel by newval (unless newval == 0) picture_of_& operator/=(PEL newval){ return assign(newval, '/'); } /// The |= operator does a pelwise absolute difference, i.e. /// destpel = abs(sourcepel-destpel) not a pelwise OR. template picture_of_& operator|=(const picture_of_& rb) { return assign(rb, '|'); } /// Replace every pel with its absolute difference from newval picture_of_& operator|=(PEL newval){ return assign(newval, '|'); } /// The ^= operator does a pelwise squared difference, i.e. /// destpel = sourcepel-destpel; destpel *= destpel; /// not a pelwise XOR. template picture_of_& operator^=(const picture_of_& rb) { return assign(rb, '^'); } /// Replace every pel with its squared difference from newval picture_of_& operator^= (PEL newval){ return assign(newval, '^'); } /// The &= operator does a pelwise conditional replacement, i.e. /// if (sourcepel < 0) destpel=destpel; else destpel = sourcepel; template picture_of_& operator&= (const picture_of_& rb) { return assign(rb, '&'); } // Following two operators were included in earlier versions of // clip for completeness, but they really have no value! picture_of_& operator&= (PEL newval){ return assign(newval, '&'); } //@} /// \name Operators combining pictures to give PEL results //@{ /// When a picture is added to a picture the result is a PEL which is the /// sum of all pelwise additions. PEL operator+ (const picture_of_& rb) { return sum(rb, '+'); } /// Return the sum of all pelwise subtractions. PEL operator-(const picture_of_& rb) { return sum(rb, '-'); } /// Return the sum of all pelwise multiplications. Can convolve a picture with /// an operator by making the operator into a picture, then multiply operator /// and a moving block of the picture together. PEL operator*(const picture_of_& rb) { return sum(rb, '*'); } /// Return the sum of all pelwise divisions. Uses divisor of 1 for any zero /// pel in rb. PEL operator/ (const picture_of_& rb) { return sum(rb, '/'); } /// Return the sum of all pelwise absolute differences. Can be used for block matching. PEL operator| (const picture_of_& rb) { return sum(rb, '|'); } /// Return the sum of all pelwise squared differences. Can be used for block matching. PEL operator^ (const picture_of_& rb) { return sum(rb, '^'); } //@} /// Returns the PEL * buffer pointer used internally by picture_of_int. THIS /// FUNCTION IS DANGEROUS. It providesno border management. Rarely used. PEL **bufptr() { return pr->buf; } /// Returns the int buffer pointer used internally by picture_of_int for /// the row i. Use this function if you need direct access to the pel values /// to implement a fast row operation. PEL *rowptr(int i) { return pr->buf[i]; } /// const version of the above for read only cases. PEL *rowptr(int i) const { return pr->buf[i]; } /// \name Functions for interfacing to a local camera //@{ /// If a camera exists, attach it to this picture. Returns negative value /// if the camera doesn't exist, could not be initialized, is already /// attached to another picture or the current picture is too small to /// have a camera attached. int attach_camera(); /// Initiate the request of a frame from the camera attached to this /// picture. Returns immediately; you must check frame_delivered() for /// arrival of the frame. int request_frame(); /// This version of request_frame() only works with Philips cameras on /// Linux. Will be extended to other cameras and platforms when possible. int request_frame(int shutterspeed, int gain); /// Immediately returns 0 if a requested frame is not yet available. If /// the frame is available does a buffer switch and returns 1. Because of /// the buffer switch within the function, immediately before the call the /// picture has its old contents; immediately after it has its the frame. int frame_delivered(); /// Call to detach the camera from the picture. You may need to do this /// in order to attach the camera to a different picture. int detach_camera(); //@} /// \name Miscellaneous matrix manipulation functions //@{ /// Pour the pels of the source picture into this picture which has the /// same number of pels but a different aspect ratio int reaspect(const picture_of_& source) { int snr = source.nrows(); int snc = source.ncols(); int dnr = nrows(); int dnc = ncols(); if ((snr == dnr)&&(snc == dnc)) { *this = source; return 0; } if (snr*snc != dnr*dnc) { cout << "Cannot reformat a " << snr << " x " << snc; cout << " image into a " << dnr << " x " << dnc; cout << " image.\nDifferent number of pels\n"; return -1; } int cnt(snr*snc); int si(source.rowrange.istart()); int sj(source.colrange.istart()); int di(rowrange.istart()); int dj(colrange.istart()); int sisp(source.rowrange.ispacing()); int sjsp(source.colrange.ispacing()); int disp(rowrange.ispacing()); int djsp(colrange.ispacing()); int siend(source.rowrange.iend()); int sjend(source.colrange.iend()); int diend(rowrange.iend()); int djend(colrange.iend()); while(cnt--) { if (sj > sjend) { si += sisp; sj = source.colrange.istart(); } if (dj > djend) { di += disp; dj = colrange.istart(); } pr->buf[di][dj] = source[si][sj]; dj += djsp; sj += sjsp; } /* int si(0), sj(0), di(0), dj(0); while(cnt--) { if (sj == snc) { si++; sj = 0; } if (dj == dnc) { di++; dj = 0; } pr->buf[di][dj++] = source[si][sj++]; } */ return 0; } /// Matrix multiply into this picture. /// The appropriate picture dimensions have to be correct. To just do a /// linear transform of a picture into another picture, see mx_transform /// below. int mx_mul(picture_of_& A, picture_of_& B) { int i, j, k; const int l(B.ncols()); const int n(B.nrows()); const int m(A.nrows()); if (l != ncols()) { cout << "Cannot do C=A*B matrix multiply: Ccols ("; cout << ncols() << ") != Bcols (" << l << ")\n"; return -1; } if (n != A.ncols()) { cout << "Cannot do C=A*B matrix multiply: Acols ("; cout << A.ncols() << ") != Brows (" << n << ")\n"; return -1; } if (m != nrows()) { cout << "Cannot do C=A*B matrix multiply: Crows ("; cout << nrows() << ") != Arows (" << m << ")\n"; return -1; } for (i = 0; i < m; i++) { for (j = 0; j < l; j++) { PEL sum = 0; for (k = 0; k < n; k++) sum += A[i][k]*B[k][j]; pr->buf[i][j] = sum; } } return 0; } /// Apply picture transform as a linear transform to source and put the /// result in this. Essentially a matrix multiplication, but source and /// this are treated as vectors. This is a very liberal function, allowing /// sizes of vectors that are smaller than the transform. int mx_transform(picture_of_& transform, picture_of_& source, const int use_transpose = 0) { int i, k; int snr = source.nrows(); int snc = source.ncols(); int sn = snr*snc; int dnr = nrows(); int dnc = ncols(); int dn = dnr*dnc; const int l(1); // Effectively a column vector int n(transform.ncols()); int m(transform.nrows()); if (use_transpose) { i = n; n = m; m = i; } if (n < sn) { cout << "Cannot do linear transform: mx cols < pels in source\n"; return -1; } if (m < dn) { cout << "Cannot do linear transform: mx rows < pels in dest\n"; return -1; } // Note that it doesn't matter if the matrix is larger than the // number of pels in the source or dest. n = sn; m = dn; int si(0), sj(0), di(0), dj(0); // cerr << "mx_transform: " << nrows() << "x" << ncols() << " = "; // cerr << transform.nrows() << "x" << transform.ncols() << " * "; // cerr << source.nrows() << "x" << source.ncols() << endl; for (i = 0; i < m; i++) { si = 0; sj = 0; PEL sum = 0; if (use_transpose) { for (k = 0; k < n; k++) { sum += transform[k][i]*source[si][sj]; if (sj == snc-1) { si++; sj = 0; } else sj++; } } else { for (k = 0; k < n; k++) { sum += transform[i][k]*source[si][sj]; if (sj == snc-1) { si++; sj = 0; } else sj++; } } pr->buf[di][dj] = sum; if (dj == dnc-1) { di++; dj = 0; } else dj++; } return 0; } /// Make this picture into the transpose of the source picture int mx_transpose(picture_of_& A) { int i, j; int l = A.nrows(); int m = A.ncols(); if (l != ncols()) { cout << "Cannot do matrix transpose: Bcols != Arows\n"; return -1; } if (m != nrows()) { cout << "Cannot do matrix transpose: Brows != Acols\n"; return -1; } for (i = 0; i < l; i++) for (j = 0; j < m; j++) pr->buf[j][i] = A[i][j]; return 0; } /// Make this picture into the matrix inverse of the source picture int mx_inverse(picture_of_& A); /// Make the first row of this picture into the eigenvalues of the symmetric /// matrix represented as source picture A. int mx_eigenvalues(picture_of_& A); /// Make the first row of this picture into the eigenvalues of the symmetric /// matrix represented as source picture A and write the corresponding /// eigenvectors to evecs int mx_eigensystem(picture_of_& A, picture_of_& evecs); /// Given a eigenvalue for the symmetric matrix represented as source /// source picture A, write the corresponding /// eigenvector into the first row of this picture int mx_eigenvector(picture_of_& A, PEL evalue); /// Solve linear equations A.x = b without inverting A. this is x; both /// this and b should be one-row pictures. int mx_solve(picture_of_& transform, picture_of_& b); /// Return the determinant of this picture, treated as a matrix PEL mx_determinant(); //@} /// \name Miscellaneous graphics functions //@{ /// Fill the picture with a horizontal ramp int hramp() { // Fill picture with a horizontal ramp for (int i = 0; i < nrows(); i++) for (int j = 0; j < ncols(); j++) pr->buf[i][j] = j; return 0; } /// Fill the picture with a vertical ramp int vramp() { for (int i = 0; i < nrows(); i++) for (int j = 0; j < ncols(); j++) pr->buf[i][j] = i; return 0; } /// Draw line of shade value from (x0,y0) to (x1,y1) int draw_line(int x0, int y0, int x1, int y1, PEL value) { int dx = x1-x0; int dy = y1-y0; if ((x0 >= 0)&&(x1 >= 0)&&(y0 >= 0)&&(y1 >= 0)&&(x0 < ncols())&& (x1 < ncols())&&(y0 < nrows())&&(y1 < nrows())) { // No clipping needed if( abs(dx)>abs(dy) ){ if( x0buf[y][x] = value; } }else{ for(int x=x0;x>x1;x--){ int y = y0 + dy * (x-x0) / dx; pr->buf[y][x] = value; } } }else{ if( y0buf[y][x] = value; } }else{ for(int y=y0;y>y1;y--){ int x = x0 + dx * (y-y0) / dy; pr->buf[y][x] = value; } } } } else { if( abs(dx)>abs(dy) ){ if( x0= ncols()) continue; int y = y0 + dy * (x-x0) / dx; if (y < 0 || y >= nrows()) continue; pr->buf[y][x] = value; } }else{ for(int x=x0;x>x1;x--){ if (x < 0 || x >= ncols()) continue; int y = y0 + dy * (x-x0) / dx; if (y < 0 || y >= nrows()) continue; pr->buf[y][x] = value; } } }else{ if( y0= nrows()) continue; int x = x0 + dx * (y-y0) / dy; if (x < 0 || x >= ncols()) continue; pr->buf[y][x] = value; } }else{ for(int y=y0;y>y1;y--){ if (y < 0 || y >= nrows()) continue; int x = x0 + dx * (y-y0) / dy; if (x < 0 || x >= ncols()) continue; pr->buf[y][x] = value; } } } } return 1; } /// Treat the picture as a drawing canvas for a /// time-series graph. There are numvals consecutive /// points to be plotted over the full width of the /// picture. The vertical values in yvals are scaled /// so that minval and maxval are the picture limits. /// If these are -1, minval and maxval are taken from /// the data. int draw_graph(const int *yvals, const int numvals, const PEL drawshade, int minval = -1, int maxval = -1); //@} /// \name Miscellaneous image processing functions //@{ /// Convolve picture other with a separable point spread function and /// put the result in this. The horizontal component of the kernel is /// given by hsize=m values (h1 h2 .. hm) stored starting at hkernel. /// The vertical component is given by vsize=n values (v1 v2 .. vn), /// and the result is to be divided by divisor=k (scaling). /// All component values must be integers. The kernel is run across the /// whole picture, and may be preceded by reflection at the boundaries /// by setting do_symmetric_extension to true. The first argument is /// not const to allow symmetric extension to be done. int sepconv(picture_of_& other, const int hsize, const PEL *hkernel, const int vsize, const PEL *vkernel, const bool do_symmetric_extension=false) { picture_of_ mid(nrows(),ncols()); if (do_symmetric_extension) other.symmetric_extension(hsize,0); mid.hconv(other,hsize,hkernel); if (do_symmetric_extension) mid.symmetric_extension(0,vsize); vconv(mid,vsize,vkernel); return 1; } /// Convolve picture other with a horizontal impulse response and put /// the result in this. The kernel values are stored starting at hkernel /// and there are hsize of them. You may precede a convolution by a call /// to symmetric_extension to reflect appropriately at the boundaries if /// required. You may need to follow a convolution with division by the /// sum of the kernel coefficients to normalize the gain. PEL hconv(const picture_of_& rb, const int hsize, const PEL *hkernel) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: picture["<buf[inr][inc+hstart]; PEL pel = 0; for (int i = 0; i < hsize; i++) pel += *p++ * *q++; accum += (pr->buf[outr][outc] = pel); } return accum; } /// Convolve picture other with a vertical impulse response and put /// the result in this. The kernel values are stored starting at vkernel /// and there are vsize of them. You may precede a convolution by a call /// to symmetric_extension to reflect appropriately at the boundaries if /// required. You may need to follow a convolution with division by the /// sum of the kernel coefficients to normalize the gain. PEL vconv(const picture_of_& rb, const int vsize, const PEL *vkernel) { if ((rowrange.length() != rb.rowrange.length())|| (colrange.length() != rb.colrange.length())) cerr << "warning: picture["<buf[1][0] - &pr->buf[0][0]); for (outr = rowrange.istart(), inr = rb.rowrange.istart(); ((outr <= rowrange.iend()) && (inr <= rb.rowrange.iend())); outr += rowrange.ispacing(), inr += rb.rowrange.ispacing()) for (outc = colrange.istart(), inc = rb.colrange.istart(); ((outc <= colrange.iend()) && (inc <= rb.colrange.iend())); outc += colrange.ispacing(), inc += rb.colrange.ispacing()) { const PEL *p = vkernel; const PEL *q = &rb.pr->buf[inr+vstart][inc]; PEL pel = 0; for (int i = 0; i < vsize; i++) { pel += *p++ * *q; q += vinc; } accum += (pr->buf[outr][outc] = pel); } return accum; } /// Convolve picture other with a gaussian filter with standard /// deviation stddev and put the result in this. The /// kernel size adapts to the scale of the gaussian. The first argument /// is not const because gaussfilter does symmetric extension on the /// source image before convolution. int gaussfilter(picture_of_& other, const double stddev) { PEL filtvals[511]; // Assume we'll never have longer filter than this int filtwidth = (int) (3*stddev*2+1); if (!(filtwidth & 1)) filtwidth++; // Use odd length filters // Now make the gaussian // cout << "Filter width = " << filtwidth << endl; double x,y; double variance = stddev*stddev; double denom = sqrt(2*3.141592)*stddev; int i; for (i = 0, x = -3*stddev; i < filtwidth; x += 1.0, i++) { y = exp(-x*x/(2.0*variance)); filtvals[i] = (PEL) (y * 100); // The * 100 is so we preserve amplitude resolution for // integer types } // for (i = 0; i < filtwidth; i++) // cout << filtvals[i] << ' '; // cout << endl; sepconv(other,filtwidth,filtvals,filtwidth,filtvals,true); *this /= (PEL)(100*100*denom*denom); return 1; } //@} /// Waits for given number of milliseconds since last call to wait() on /// any picture_of_int or colour_picture_of_int. Returns actual number of /// milliseconds waited (negative if interval has already passed and /// therefore wait() returned immediately). int wait(const int milliseconds); }; /// Supports operations on colour pictures via picture_of_int components. /// colour_picture_of_ maintains four picture_of_int components called mono, /// red, green and blue. /// For any colour_picture_of_ either mono alone is used, or red, green /// and blue (but not mono) are used. Which of these two cases applies /// is determined at construction time and can't be changed. /// colour_picture_of_ has many functions equivalent /// to picture_of_'s, except that they /// operate over all three of red, /// green and blue if the colour_picture_of_ /// was constructed as colour and over /// mono if the colour_picture_of_ was /// constructed as monochrome. /// Note that colour_picture_of_ omits the /// following picture_of_int functions: /// (1) operator[](const int selector), /// i.e. You can't do something like /// a[128][128] = 40; /// where a is a colour_picture_of_. /// Instead you must do: /// if (a.ismonochrome()) /// a.mono[128][128] = 40; /// else { /// a.red[128][128] = 40; /// a.green[128][128] = 40; /// a.blue[128][128] = 40; /// }. /// (2) operator[](const double selector), /// although map() does work for the /// the purpose of interpolating one /// colour_picture_of_ from another. /// (3) showascolour(const char *comment) /// and the related reshow functions /// because a colour_picture_of_ already /// _is_ colour! /// (4) mask(const picture_of_& rb,...); /// if you are masking, you need to /// do it on monochrome images. /// (5) operator==(PEL testval) /// is another operation that only makes /// sense on monochrome images. /// (6) bufptr() /// because colour_picture_of_s have more /// than one buffer. /// (7) rowptr(int i) /// because colour_picture_of_s have more /// that one row pointer per row. /// (8) the text constructor, /// so you must create text via a /// picture_of_ construction. /// (9) the matrix functions. /// (10) The specialized image processing functions for convolutions and the /// drawing functions (lines, graphs and ramps) are not yet supported. /// All other functions are supported. template class colour_picture_of_ { public: picture_of_ mono; picture_of_ green; picture_of_ red; picture_of_ blue; // Private part of colour_picture_of_int class private: // Following data items are used for sequence processing: bool seq; int numfiles; int currfile; bool usingcam; int firstcycle; char **av; colour_picture_of_ *backup; struct clip_internal::CImgDisplay *qxp; int puremono; colour_picture_of_& assign(const PEL& newval, const char& sel) { if (!puremono) { green.assign(newval,sel); red.assign(newval,sel); blue.assign(newval,sel); } else mono.assign(newval,sel); return *this; } // Assignment for colour_picture with different data type of pels template colour_picture_of_& assign(const colour_picture_of_& rb, const char& sel) { if (!puremono) { if (rb.ismonochrome()) { green.assign(rb.mono,sel); red.assign(rb.mono,sel); blue.assign(rb.mono,sel); } else { green.assign(rb.green,sel); red.assign(rb.red,sel); blue.assign(rb.blue,sel); } } else if (!rb.ismonochrome()) { if (sel == '=') { mono.assign(rb.green,'='); mono.assign(rb.red,'+'); mono.assign(rb.blue,'+'); mono.assign(3,'/'); } else { cerr << "Sorry: I can't combine in colour with a colour_picture_of_\n"; cerr << "that was forced to monochrome during construction.\n"; cerr << "Make the source monochrome or construct the\n"; cerr << "destination so that it is colour.\n"; } } else mono.assign(rb.mono,sel); return *this; } // Assignment for colour_picture with same type of pels colour_picture_of_& assign(const colour_picture_of_& rb) { if (!puremono) { if (rb.ismonochrome()) { green.assign(rb.mono); red.assign(rb.mono); blue.assign(rb.mono); } else { green.assign(rb.green); red.assign(rb.red); blue.assign(rb.blue); } } else if (!rb.ismonochrome()) { mono.average_assign(rb.green,rb.red,rb.blue); } else mono.assign(rb.mono); return *this; } PEL sum(const colour_picture_of_& rb, const char& sel) { PEL retval; if (puremono != rb.puremono) { cerr<< "Sorry: I can't sum up operations between a normal\n"; cerr<< "colour picture and one that was forced to monochrome\n"; cerr<< "during construction. Construct the two pictures in\n"; cerr << "the same way.\n"; retval = 0; } else if (!puremono) { retval = green.sum(rb.green,sel); retval += red.sum(rb.red,sel); retval += blue.sum(rb.blue,sel); } else retval = mono.sum(rb.mono,sel); return retval; } // Following constructor sets up dereferenced version colour_picture_of_(const irange& range, const colour_picture_of_& source) { qxp = 0; // Never show a dereferenced version backup = 0; seq = 0; puremono = source.puremono; mono.dereffrom(range, source.mono); red.dereffrom(range, source.red); green.dereffrom(range, source.green); blue.dereffrom(range, source.blue); } int construct_or_read_from_file(const char *pnmname, const int bord = DEFAULT_BORDER, const int readflag = 1); // And the part accessible by applications public: /// Returns 1 if this picture was constructed as monochrome (e.g. /// because it was read it from a PGM file), returns 0 if truly colour. /// USER FUNCTIONS THAT OPERATE AT THE PEL LEVEL ON colour_picture_of_ OBJECTS /// must always test this. E.g., to read in a picture then put a white /// dot in the centre: colour_picture_of_ in(argv[1]); /// int nr = in.nrows(); int nc = in.ncols(); /// if (in.ismonochrome()) in.mono[nr/2][nc/2] = 255; /// else { in.green[nr/2][nc/2] = 255; /// in.red[nr/2][nc/2] = 255; in.blue[nr/2][nc/2] = 255; /// } int ismonochrome() const { return puremono; } /// Instantiates a colour picture by reading it in to the green, red /// and blue picture_of_s from a file. If the file is monochrome, it /// is read in to the mono picture_of_ and all subsequent operations are /// done in mono only. The bord argument works as in picture_of_. /// CLIP will read the following file formats: PGM, PPM, JPEG, APT and the /// most common types of BMP. colour_picture_of_(const char *pnmname, const int bord=DEFAULT_BORDER) { qxp = 0; backup = 0; seq = 0; if (construct_or_read_from_file(pnmname, bord, 0) >= 0) return; char errmsg[180]; sprintf(errmsg,"CLIP cannot read\n%s\nas a picture",pnmname); clip::picture_of_ temp(errmsg,0,255); int r=temp.nrows(); int c=temp.ncols(); puremono = 1; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); mono = temp; } /// Instantiates a zeroed colour picture of given dimensions. Usually sets up /// red, green and blue picture_of_s but if forcemono is nonzero, sets /// up mono only. colour_picture_of_(const int r, const int c, const int bord=DEFAULT_BORDER, const int forcetomono=0) { qxp = 0; backup = 0; seq = 0; if (forcetomono) { // cout << "Colour picture being forced to monochrome\n"; puremono = 1; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); return; } puremono = 0; mono.initialize(r, c, bord); red.initialize(r, c, bord); green.initialize(r, c, bord); blue.initialize(r, c, bord); } /// Conventional copy constructor template colour_picture_of_ (const colour_picture_of_& source) { using clip_internal::pic_rep; qxp = 0; backup = 0; seq = 0; puremono = source.puremono; // Copy everything except the pic_rep explicitly mono.qxp = 0; red.qxp = 0; green.qxp = 0; blue.qxp = 0; mono.dereferenced = source.mono.dereferenced; mono.rowrange = source.mono.rowrange; mono.colrange = source.mono.colrange; red.dereferenced = source.red.dereferenced; red.rowrange = source.red.rowrange; red.colrange = source.red.colrange; green.dereferenced = source.green.dereferenced; green.rowrange = source.green.rowrange; green.colrange = source.green.colrange; blue.dereferenced = source.blue.dereferenced; blue.rowrange = source.blue.rowrange; blue.colrange = source.blue.colrange; // So far haven't copied the pic_reps and don't want to if // we are dereferenced if (puremono) { if (mono.dereferenced) mono.pr = (pic_rep *) source.mono.pr; // Above cast is ok because never dereference to // a different type. else mono.pr = new pic_rep(*source.mono.pr); red.pr = green.pr = blue.pr = mono.pr; } else { if (red.dereferenced) { mono.pr = (pic_rep *) source.mono.pr; // Not really necessary red.pr = (pic_rep *) source.red.pr; blue.pr = (pic_rep *) source.blue.pr; green.pr = (pic_rep *) source.green.pr; // Above casts are ok because never dereference to // a different type. } else { mono.initialize(mono.rowrange.length(), mono.colrange.length(), source.mono.pr->border); red.pr = new pic_rep(*source.red.pr); green.pr = new pic_rep(*source.green.pr); blue.pr = new pic_rep(*source.blue.pr); } } } // And the special case of constructing from same type that VC doesn't seem to // deal with automatically. // template <> colour_picture_of_ (const colour_picture_of_& source) { using clip_internal::pic_rep; qxp = 0; backup = 0; seq = 0; puremono = source.puremono; // Copy everything except the pic_rep explicitly mono.qxp = 0; red.qxp = 0; green.qxp = 0; blue.qxp = 0; mono.dereferenced = source.mono.dereferenced; mono.rowrange = source.mono.rowrange; mono.colrange = source.mono.colrange; red.dereferenced = source.red.dereferenced; red.rowrange = source.red.rowrange; red.colrange = source.red.colrange; green.dereferenced = source.green.dereferenced; green.rowrange = source.green.rowrange; green.colrange = source.green.colrange; blue.dereferenced = source.blue.dereferenced; blue.rowrange = source.blue.rowrange; blue.colrange = source.blue.colrange; // So far haven't copied the pic_reps and don't want to if // we are dereferenced if (puremono) { if (mono.dereferenced) mono.pr = source.mono.pr; else mono.pr = new pic_rep(*source.mono.pr); red.pr = green.pr = blue.pr = mono.pr; } else { if (red.dereferenced) { mono.pr = source.mono.pr; // Not really necessary red.pr = source.red.pr; blue.pr = source.blue.pr; green.pr = source.green.pr; } else { mono.initialize(mono.rowrange.length(), mono.colrange.length(), source.mono.pr->border); red.pr = new pic_rep(*source.red.pr); green.pr = new pic_rep(*source.green.pr); blue.pr = new pic_rep(*source.blue.pr); } } } /// Creates a colour_picture_of_ from a picture_of_, so loads up mono only. template colour_picture_of_ (const picture_of_& source) { qxp = 0; backup = 0; seq = 0; int r, c; int bord; if (source.dereferenced) { cerr << "Sorry. You cannot construct a colour_picture_of_ from an\n"; cerr << "irange-dereferenced picture_of_. Use assignment.\n"; exit(1); } puremono = 1; r = source.rowrange.length(); c = source.colrange.length(); bord = source.pr->border; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); mono.assign(source,'='); } /// Modify-source constructor that applies callback point function (*f) /// over source rb. If rb is mono, then so is this picture. colour_picture_of_ (const colour_picture_of_& source, PEL (*f)(PEL&, const PEL&)) { qxp = 0; backup = 0; seq = 0; int forcetomono = source.ismonochrome(); int r, c; int bord; if (forcetomono) { puremono = 1; r = source.mono.rowrange.length(); c = source.mono.colrange.length(); bord = source.mono.pr->border; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); } else { puremono = 0; r = source.red.rowrange.length(); c = source.red.colrange.length(); bord = source.red.pr->border; mono.initialize(r, c, bord); red.initialize(r, c, bord); green.initialize(r, c, bord); blue.initialize(r, c, bord); } point(source, f); } /// Modify-source constructor that applies callback region function (*f) /// over source rb. If rb is mono, then so is this picture. colour_picture_of_(colour_picture_of_& source, PEL(*f)(PEL&, const PEL **)) { qxp = 0; backup = 0; seq = 0; int forcetomono = source.ismonochrome(); int r, c; int bord; if (forcetomono) { puremono = 1; r = source.mono.rowrange.length(); c = source.mono.colrange.length(); bord = source.mono.pr->border; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); } else { puremono = 0; r = source.red.rowrange.length(); c = source.red.colrange.length(); bord = source.red.pr->border; mono.initialize(r, c, bord); red.initialize(r, c, bord); green.initialize(r, c, bord); blue.initialize(r, c, bord); } neigh(source, f); } /// Read picture into this object. If this object was constructed as /// colour and the file is monochrome, will copy the /// monochrome values to all three of red, green and blue. /// If this object was constructed as monochrome and the file is colour, /// read() will refuse to convert. int read(const char *ppmname); /// Write picture to PPM file (unless it was constructed as monochrome /// in which case a PGM file will be written. int write(const char *ppmname); /// Write picture in APT format 0 <= quality <= 100. When quality is /// 100, coding is lossless int aptwrite(const char *aptname, const int quality = 100); /// Write picture in BMP format. If you must. int bmpwrite(const char *bmpname); /// Destructor ~colour_picture_of_() { // cerr << "colour picture delete\n"; if (seq && backup) delete backup; if (qxp) delete qxp; if (puremono) { // We have to set up pseudopics now so that // picture_of_ delete works ok red.initialize(1,1,0); green.initialize(1,1,0); blue.initialize(1,1,0); } } /// Copies picture_of_ into colour_picture_of_. If this object was /// constructed as colour, will copy the rb value into red, green and blue. template colour_picture_of_& operator= (const picture_of_& rb) { if (puremono) mono.assign(rb,'='); else { green.assign(rb,'='); red.assign(rb,'='); blue.assign(rb,'='); } return *this; } /// Apply callback point function *f() over picture, modifying itself. PEL point(PEL (*f)(PEL&)) { PEL retval; if (puremono) return mono.point(f); retval = red.point(f); retval += green.point(f); retval += blue.point(f); return retval; } /// Apply callback point function *f() over source picture rb, putting /// result into *this. PEL point(const colour_picture_of_& rb, PEL (*f)(PEL&, const PEL&)) { PEL retval; if (!puremono) { if (rb.puremono) { retval = green.point(rb.mono,f); retval += red.point(rb.mono,f); retval += blue.point(rb.mono,f); } else { retval = green.point(rb.green,f); retval += red.point(rb.red,f); retval += blue.point(rb.blue,f); } } else if (!rb.puremono) { cerr << "Sorry: I can't do a colour point operation to a "; cerr << "colour_picture_of_\n"; cerr << "that was forced to monochrome during construction.\n"; cerr << "Make the source monochrome or construct the\n"; cerr << "destination so that it is colour.\n"; retval = 0; } else retval = mono.point(rb.mono,f); return retval; } /// Apply callback neigh function *f() over source picture rb, putting /// result into this. PEL neigh(colour_picture_of_& rb, PEL(*f)(PEL&, const PEL **)) { PEL retval; if (!puremono) { if (rb.puremono) { retval = green.neigh(rb.mono,f); retval += red.neigh(rb.mono,f); retval += blue.neigh(rb.mono,f); } else { retval = green.neigh(rb.green,f); retval += red.neigh(rb.red,f); retval += blue.neigh(rb.blue,f); } } else if (!rb.puremono) { cerr << "Sorry: I can't do a colour neigh operation to a "; cerr << "colour_picture_of_\n"; cerr << "that was forced to monochrome during construction.\n"; cerr << "Make the source monochrome or construct the\n"; cerr << "destination so that it is colour.\n"; retval = 0; } else retval = mono.neigh(rb.mono,f); return retval; } /// \name Assignment operators //@{ /// Set this picture equal to source picture with different pel data type. /// This and all assignments /// work with irange-referenced pictures. template colour_picture_of_& operator=(const colour_picture_of_& rb) { return assign(rb, '='); } /// Set this picture equal to source picture with same pel data type. // template <> colour_picture_of_& operator=(const colour_picture_of_& rb) { return assign(rb); } /// Set every pel to newval. E.g.irange rg(0,8,256-8); /// picture_of_ a(256,256); a[rg][rg] = 255; /// sets every eighth pel horizontally and vertically to white. colour_picture_of_& operator=(PEL newval){ return assign(newval, '='); } /// Add source picture to this picture. I.e. each pel value in rb is /// added to the corresponding pel's value in this. template colour_picture_of_& operator+=(const colour_picture_of_& rb) { return assign(rb, '+'); } /// Add newval to every pel (in all components) colour_picture_of_& operator+=(PEL newval){ return assign(newval, '+'); } /// Subtract source picture from this picture. I.e. each pel value /// in rb is subtracted from the corresponding pel's value in this. template colour_picture_of_& operator-=(const colour_picture_of_& rb) { return assign(rb, '-'); } /// Subtract newval from every pel (in all components) colour_picture_of_& operator-=(PEL newval){ return assign(newval, '-'); } /// Multiple source picture with this picture. I.e. each pel value /// in rb is multiplied with the corresponding pel's value in this. template colour_picture_of_& operator*=(const colour_picture_of_& rb) { return assign(rb, '*'); } /// Multiple every pel by newval (in all components) colour_picture_of_& operator*=(PEL newval){ return assign(newval, '*'); } /// If a pel in rb is 0, divides the corresponding pel in this /// by 1, otherwise divides it by rb pel value. template colour_picture_of_& operator/=(const colour_picture_of_& rb) { return assign(rb, '/'); } /// Divide every pel by newval (unless newval == 0) colour_picture_of_& operator/=(PEL newval){ return assign(newval, '/'); } /// The |= operator does a pelwise absolute difference, i.e. /// destpel = abs(sourcepel-destpel) not a pelwise OR. template colour_picture_of_& operator|=(const colour_picture_of_& rb) { return assign(rb, '|'); } /// Replace every pel with its absolute difference from newval colour_picture_of_& operator|=(PEL newval){ return assign(newval, '|'); } /// The ^= operator does a pelwise squared difference, i.e. /// destpel = sourcepel-destpel; destpel *= destpel; /// not a pelwise XOR. template colour_picture_of_& operator^=(const colour_picture_of_& rb) { return assign(rb, '^'); } /// Replace every pel with its squared difference from newval colour_picture_of_& operator^=(PEL newval){ return assign(newval, '^'); } /// The &= operator does a pelwise conditional replacement, i.e. /// if (sourcepel < 0) destpel=destpel; else destpel = sourcepel; template colour_picture_of_& operator&=(const colour_picture_of_& rb) { return assign(rb, '&'); } // Following two operators were included in earlier versions of // clip for completeness, but they really have no value! colour_picture_of_& operator&=(PEL newval){ return assign(newval, '&'); } //@} /// \name Operators combining pictures to give PEL results //@{ /// When a picture is added to a picture the result is a PEL which is the /// sum of all pelwise additions. PEL operator+(const colour_picture_of_& rb) { return sum(rb, '+'); } /// Return the sum of all pelwise subtractions. PEL operator-(const colour_picture_of_& rb) { return sum(rb, '-'); } /// Return the sum of all pelwise multiplications. Can convolve a picture with /// an operator by making the operator into a picture, then multiply operator /// and a moving block of the picture together. PEL operator*(const colour_picture_of_& rb) { return sum(rb, '*'); } /// Return the sum of all pelwise divisions. Uses divisor of 1 for any zero /// pel in rb. PEL operator/(const colour_picture_of_& rb) { return sum(rb, '/'); } /// Return the sum of all pelwise absolute differences. Can be used for block matching. PEL operator|(const colour_picture_of_& rb) { return sum(rb, '|'); } /// Return the sum of all pelwise squared differences. Can be used for block matching. PEL operator^(const colour_picture_of_& rb) { return sum(rb, '^'); } //@} /// \name Dereferencing operators /// The following operator[]s are used to dereference (i.e. index) pictures. /// They must always be appended to a colour_picture_of_ in pairs, with the /// first [] specifying a row value or range and the second [] specifying /// a column value or range. //@{ /// When dereferenced (i.e. indexed) with irange operators, a subimage or /// block of the picture is returned as a picture. E.g. irange rg(0,1,7); /// colour_picture_of_ pic(256,256); pic[rg][rg] = 255; sets the top left 8x8 square /// in pic to white. colour_picture_of_ operator[](const irange& selector) { colour_picture_of_ m(selector,*this); return m; } /// We often want to paste one picture on another, centred at a particular /// location. Can do this with irange derefencings, but this way is easier. int paste_centred_on (const picture_of_& from, const int x, const int y) { irange r(0,1,from.nrows()-1); irange c(0,1,from.ncols()-1); r -= from.nrows()/2; c -= from.ncols()/2; r += y; c += x; if (puremono) mono[r][c].assign(from,'='); else { red[r][c].assign(from,'='); green[r][c].assign(from,'='); blue[r][c].assign(from,'='); } // operator[](r).operator[](c).assign(from,'='); return 0; } /// As above, for colour_picture_of_ pasting int paste_centred_on (const colour_picture_of_& from, const int x, const int y) { irange r(0,1,from.nrows()-1); irange c(0,1,from.ncols()-1); r -= from.nrows()/2; c -= from.ncols()/2; r += y; c += x; operator[](r).operator[](c).assign(from,'='); return 0; } //@} /// \name Picture display functions /// Use these functions to show or reshow the current picture on the screen //@{ /// Display the picture on the screen in its own window, with /// the comment string as title. If normalize is true values are scaled /// to [0,255] for display. If false, it is assumed they already are in this range. int show(const char *comment, const bool normalize = 1); /// Display picture without a specific title int show(const bool n = 1) { return show("CLIP image", n); } /// Display without decorations at Nx,Ny. If parent==0, the coords are /// screen coordinates, and the window receives events (e.g. pointx()) /// as normal. If parent is a displayed picture, the coords are relative to /// that picture and a child window is displayed that does not receive events. int showat(const int Nx, const int Ny, const bool normalize, const picture_of_ *parent); /// Display without decorations, with colour_picture_of_ as parent int showat(const int Nx, const int Ny, const bool normalize, const colour_picture_of_ *parent); /// Display without decorations at screen coords Nx, Ny int showat(const int Nx, const int Ny, const bool normalize) { return showat(Nx,Ny,normalize,(const picture_of_ *)0); } /// Display without decorations at screen coords Nx, Ny int showat(const int Nx, const int Ny) { return showat(Nx,Ny,1,(const picture_of_ *)0); } /// Redisplay the picture (presumably because it has changed) and change /// title to the comment string. int reshow(const char *comment, const bool normalize = 1); /// Redisplay the picture without altering the title. int reshow(const bool normalize = 1); /// Redisplay without normalization. Equivalent to reshow(comment,0); int fastreshow(const char *comment); /// Redisplay without normalization. Equivalent to reshow(0); int fastreshow(); /// Change title of the displayed pic. int rename(const char *comment); /// Stop showing the picture int unshow(); /// Returns non-zero if the user has left-clicked in the close box of the /// window where the picture is being displayed. int closerequested(); /// Return current pointer (mouse) x coord relative to left of picture window int pointx(); /// Return current pointer (mouse) y coord relative to top of picture window int pointy(); /// Return non-zero if the mouse's left button is pressed within the picture's window. int pointleftbutton(); /// Returns non-zero if mid button down int pointmiddlebutton(); /// Returns non-zero if right button down int pointrightbutton(); /// Returns non-zero if the shift key is held down while a mouse button is down. int pointshiftbutton(); /// Returns keycode of any key held down int key(); /// Clears keystroke so that it will not be reprocessed. int clearkey(); /// Inspect a shown picture. Until the right button is clicked inside /// the picture, the program pauses and allows the user to /// left-click within the picture and inspect the values at that point. int inspect() { if (!qxp) return -1; char infostring[64]; while (pointrightbutton()); // From any previous rightbutton press int prevx = -1; int prevy = -1; while(1) { if (pointrightbutton()) { // No longer do a rename back to what was before return 1; } if (!pointleftbutton()) continue; int x = pointx(); int y = pointy(); if ((x == prevx)&&(y == prevy)) continue; prevx = x; prevy = y; if ((x < 0)||(x >= ncols())||(y < 0)||(y >= nrows())) { // Beyond picture limits rename("pointer outside pic"); continue; // Back to start of loop } // Else pointer is within picture limits if (puremono) { float pelvalue = (float) mono.pr->buf[y][x]; sprintf(infostring,"value at (%d,%d) = %f", x,y,pelvalue); } else { float redvalue = (float) red.pr->buf[y][x]; float greenvalue = (float) green.pr->buf[y][x]; float bluevalue = (float) blue.pr->buf[y][x]; sprintf(infostring,"value at (%d,%d) = %f,%f,%f", x,y,redvalue,greenvalue,bluevalue); } rename(infostring); // Update header } } //@} /// Set values in the border of the picture by copying the values on /// the edges of the picture outwards. void dc_pad() { if (puremono) mono.dc_pad(); else { red.dc_pad(); green.dc_pad(); blue.dc_pad(); } } /// Set values in the border of the picture to zero. void zero_border() { if (puremono) mono.zero_border(); else { red.zero_border(); green.zero_border(); blue.zero_border(); } } /// Return number of rows in picture int nrows() const { return mono.nrows(); } /// Return number of columns in picture int ncols() const { return mono.ncols(); } /// Return width of border int bordersize() { return mono.bordersize(); } /// Return maximum value in picture PEL max() const { if (puremono) return mono.max(); PEL r = red.max(); PEL g = green.max(); PEL b = blue.max(); if (g > r) r = g; if (b > r) r = b; return r; } /// Clip picture at given maximum value void max(const PEL clipval) { if (puremono) mono.max(clipval); else { green.max(clipval); red.max(clipval); blue.max(clipval); } } /// Return minimum value in picture PEL min() const { if (puremono) return mono.min(); PEL r = red.min(); PEL g = green.min(); PEL b = blue.min(); if (g < r) r = g; if (b < r) r = b; return r; } /// Clip picture at given minimum value void min(const PEL clipval) { if (puremono) mono.min(clipval); else { green.min(clipval); red.min(clipval); blue.min(clipval); } } /// Return total of all pel values PEL total() const { if (puremono) return mono.total(); return (red.total() + green.total() + blue.total()); } /// Reranges or normalizes picture values to fall in given range /// If scale_even_if_inside_range is false (default), then if all /// values are current between the give newlow and newhigh, they /// are unaltered. Otherwise, values are scaled so that the new /// max() will be newhigh and the new min() will be newmin. int rerange(const PEL newlow, const PEL newhigh, const bool scale_even_if_inside_range = 0) { if (newlow == newhigh) { *this = newhigh; return 0; } PEL mx = max(); PEL mn = min(); if ((mx == newhigh) && (mn == newlow)) return 0; if (!scale_even_if_inside_range && (mx <= newhigh) && (mn >= newlow)) return 0; if (mx == mn) { if (mx > 0) *this = newhigh; else *this = newlow; return 0; } PEL newrange = newhigh - newlow; PEL oldrange = mx - mn; if (puremono) { for (int i = 0; i < nrows(); i++) { PEL *pel = mono.pr->buf[i]; for (int j = 0; j < ncols(); j++) { *pel = newlow + (newrange*(*pel-mn))/oldrange; pel++; } } } else { for (int i = 0; i < nrows(); i++) { PEL *r = red.pr->buf[i]; PEL *g = green.pr->buf[i]; PEL *b = blue.pr->buf[i]; for (int j = 0; j < ncols(); j++) { *r = newlow + (newrange*(*r-mn))/oldrange; *g = newlow + (newrange*(*g-mn))/oldrange; *b = newlow + (newrange*(*b-mn))/oldrange; r++, g++, b++; } } } return 1; } /// \name Sequence processing facilities /// Sequence processing facilities for "standard" CLIP programs. /// Many CLIP programs consist of a processing loop that cycles over /// input pictures from a sequence of named files or from a camera. //@{ /// Sequence processing constructor that interprets main()'s parameters. /// If the command line had no arguments, tries to attach a camera. /// If that fails, generates a default picture. /// If the command line had arguments, interprets these as picture /// files and sets up for reading these in sequence. /// See also member function next() colour_picture_of_(int argc, char *argv[], const int def_nrows=240, const int def_ncols=320, const int bord=DEFAULT_BORDER) { seq = 1; // Flag to say sequence processing numfiles = argc - 1; currfile = 1; usingcam = 0; firstcycle = 1; av = argv; qxp = 0; backup = 0; if (!numfiles) { // If there are no arguments, try to open a camera and, if // that fails, create a default image puremono = 0; mono.initialize(def_nrows, def_ncols, bord); red.initialize(def_nrows, def_ncols, bord); green.initialize(def_nrows, def_ncols, bord); blue.initialize(def_nrows, def_ncols, bord); if (attach_camera() < 0) { red.vramp(); blue.hramp(); green = 128; clip::picture_of_ text("Default image",0,128,3); green.paste_centred_on(text, def_ncols/2,def_nrows/2); } else { request_frame(); usingcam = 1; } return; } // Otherwise, open the first file if (construct_or_read_from_file(argv[1], bord, 0) >= 0) { seq = 1; if (numfiles == 1) // Make and save a backup for fast reread backup = new colour_picture_of_(*this); return; } qxp = 0; char errmsg[180]; sprintf(errmsg,"CLIP cannot read\n%s\nas a picture",argv[1]); clip::picture_of_ temp(errmsg,0,255); int r=temp.nrows(); int c=temp.ncols(); puremono = 1; mono.initialize(r, c, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); mono = temp; if (numfiles == 1) // Make and save a backup for fast reread backup = new colour_picture_of_(*this); } /// Loads the next picture in the sequence into this picture that was /// constructed with the sequence processing constructor. Returns 1 if /// the picture has been seen before (if cycling back to the start of /// the argument list) and 0 if it has not. Returns -1 if the picture /// was not constructed using the sequence processing constructor. int next() { if (!seq) // Not set up to do sequence processing { cerr << "next() called for a picture not constructed for "; cerr << "sequence processing." << endl; return -1; } if (usingcam) { while(!frame_delivered()) ; request_frame(); return 1; // Not seen this picture before } if (!numfiles){ // Using default picture // Remake it, just in case it was overwritten red.vramp(); blue.hramp(); green = 128; clip::picture_of_ text("Default image",0,128,3); green.paste_centred_on(text,ncols()/2,nrows()/2); return 0; } if (numfiles == 1){ // We made a backup of this one to save // rereading every cycle if (puremono) mono = backup->mono; else { red = backup->red; blue = backup->blue; green = backup->green; } return 0; } if (currfile > numfiles) { currfile = 1; // Back to start firstcycle = 0; } read(av[currfile++]); return firstcycle; } //@} /// The map operators provide a fast way of mapping a bilinearly interpolated /// value from one picture into another. transparency is a number from 0 (overwrite) /// to 255 (preserve almost all of what was there originally). So with two /// colour_picture_of_s's source and dest, dest.map(50,76,source,5.3,99.99,0); /// is exactly equivalent to dest.comp[76][50] = source.comp[99.99][5.3]; for /// comp = {red, green, blue} or comp = {mono}, but much faster. void map(const int myx,const int myy,const colour_picture_of_& from, const double fromx,const double fromy, const int transparency); /// This map operator is even faster than the double version above. This /// time fromx and fromy are both 256 times the actual values desired, so /// dest.map(50,76,source,64,384,0); is the same as /// dest.map(50,76,source,0.25,1.5,0); but faster. Rarely used. void map(const int myx,const int myy,const colour_picture_of_& from, const int fromx,const int fromy, const int transparency); /// \name Functions for interfacing to a local camera //@{ /// If a camera exists, attach it to this picture. Returns negative value /// if the camera doesn't exist, could not be initialized, is already /// attached to another picture or the current picture is too small to /// have a camera attached. int attach_camera(); /// Initiate the request of a frame from the camera attached to this /// picture. Returns immediately; you must check frame_delivered() for /// arrival of the frame. int request_frame(); /// This version of request_frame() only works with Philips cameras on /// Linux. Will be extended to other cameras and platforms when possible. int request_frame(int shutterspeed, int gain); /// Immediately returns 0 if a requested frame is not yet available. If /// the frame is available does a buffer switch and returns 1. Because of /// the buffer switch within the function, immediately before the call the /// picture has its old contents; immediately after it has its the frame. int frame_delivered(); /// Call to detach the camera from the picture. You may need to do this /// in order to attach the camera to a different picture. int detach_camera(); //@} /// Waits for given number of milliseconds since last call to wait() on /// any picture_of_ or colour_picture_of_. Returns actual number of /// milliseconds waited (negative if interval has already passed and /// therefore wait() returned immediately). int wait(const int milliseconds); // Have to deal with friends differently because of MSC VC++ problem #if ( defined(_MSC_VER)) friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; friend class colour_picture_of_; #else template friend class picture_of_; template friend class colour_picture_of_; #endif }; // Finally the stuff on double_dereferenced pictures. // Not to be used by applications. namespace clip_internal { template class double_dereferenced_picture_of_ { PEL *abovep; PEL *belowp; double a; public: double_dereferenced_picture_of_(const double& r, picture_of_ *source) { int top = (int) r; if (r < 0) top--; a = r - top; abovep = source->pr->buf[top++]; belowp = source->pr->buf[top]; } PEL operator[](const double& c) { int left = (int) c; if (c < 0) left--; double b = c - left; int y00 = abovep[left]; int y10 = belowp[left++]; int y01 = abovep[left]; int y11 = belowp[left]; return (PEL)(a*b*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); } }; } // namespace clip_internal /// Supports parameter control through interaction on picture windows /// parampic is a particular kind of colour_picture_of_ providing easy facilities /// for a programmer to display one- and two-dimensional sliders via CLIP. /// It can be supplied with a background picture, or use default ramps, then /// it displays a hairline slider over the background. The range of the /// slider is set during construction, and thereafter, a user clicking in /// the picture moves the slider to the appropriate scaled value. The program /// can then easily read off the current value of the parameter(s) /// represented by the parampic. It is assumed that the parameter read /// functions are called often when input is expected because sensing of pointer /// (mouse) state and updating are only done when the functions are called. template class parampic_of_{ colour_picture_of_ *bg; colour_picture_of_ *shown; bool bg_is_mine; int lastx, lasty; int type; double scalex, scaley; double lowx, lowy; double x, y; // Current values void paintvalues(double xval, double yval) { char text[128]; if (type == 3) sprintf(text,"(%.3f,%.3f)",xval,yval); else if (type == 2) sprintf(text,"(%.3f)",yval); else sprintf(text,"(%.3f)",xval); picture_of_ t(text,255,-1); irange r(0,1,t.nrows()-1); irange c(0,1,t.ncols()-1); if (shown->ismonochrome()) shown->mono[r][c] &= t; else { shown->red[r][c] &= t; shown->green[r][c] &= t; shown->blue[r][c] &= t; } } void paintcross(int x, int y) { irange yr(0,2,shown->nrows()-1); irange xr(0,2,shown->ncols()-1); irange thickness(0,0,0); if (shown->ismonochrome()) { if (type & 1){ shown->mono[yr][thickness+x] = 0; shown->mono[yr+1][thickness+x] = 255; } if (type & 2){ shown->mono[thickness+y][xr] = 0; shown->mono[thickness+y][xr+1] = 255; } } else { if (type & 1) { shown->red[yr][thickness+x] = 0; shown->green[yr][thickness+x] = 0; shown->blue[yr][thickness+x] = 0; shown->red[yr+1][thickness+x] = 255; shown->green[yr+1][thickness+x] = 255; shown->blue[yr+1][thickness+x] = 255; } if (type & 2) { shown->red[thickness+y][xr] = 0; shown->green[thickness+y][xr] = 0; shown->blue[thickness+y][xr] = 0; shown->red[thickness+y][xr+1] = 255; shown->green[thickness+y][xr+1] = 255; shown->blue[thickness+y][xr+1] = 255; } } } // init is called by the constructors void init (colour_picture_of_ *background, const double xstart, const double xend, const double xfirst, const double ystart, const double yend, const double yfirst, const char *name) { if (ystart == yend) type = 1; // Horizonal slider only else if (xstart == xend) type = 2; // Vertical slider only else type = 3; // 2D if (!background) { if (type == 1) { bg = new colour_picture_of_(32,256,0,1); bg->mono.hramp(); } else if (type == 2) { bg = new colour_picture_of_(256,64,0,1); // Vertical sliders are wider than horiz // ones are tall so there's room for // a caption bg->mono.vramp(); } else { bg = new colour_picture_of_(256,256,0,0); bg->red.hramp(); bg->green.vramp(); bg->blue = 128; } bg_is_mine = true; } else { bg = background; bg_is_mine = false; } shown = new colour_picture_of_(*bg); lowx = xstart; lowy = ystart; double xrange = xend-xstart; double yrange = yend-ystart; scalex = xrange/bg->ncols(); scaley = yrange/bg->nrows(); x = xfirst; y = yfirst; lastx = (int)((xfirst-xstart)/scalex); lasty = (int)((yfirst-ystart)/scaley); paintcross(lastx,lasty); paintvalues(x,y); shown->show(name); } public: /// Create a parameter picture with the given background or a default if the /// background pointer argument = 0, with X,Y control ranges as specified. /// The X parameter value can vary between xstart and xend, with initial value /// xfirst; the Y parameter value can vary between ystart and yend, with initial /// value yfirst. If xstart == xend, the slider is 1D and vertical; /// if ystart == yend, the slider is 1D and horizontal. Otherwise, both X and Y /// can be varied. parampic_of_(colour_picture_of_ *background, const double xstart, const double xend, const double xfirst, const double ystart, const double yend, const double yfirst, const char *name) { init(background,xstart,xend,xfirst,ystart,yend,yfirst,name); } /// Create the most common kind of parameter picture. 1D in X with default /// background. parampic_of_(const double xstart, const double xend, const double xfirst, const char *name) { init(0,xstart,xend,xfirst,0,0,0,name); } /// Destructor ~parampic_of_() { delete shown; if (bg_is_mine) delete bg; } /// Get current X and Y values into xout and yout. Returns 1 if there has /// been a change, 0 otherwise. int XY(double &xout, double &yout) { xout = x; yout = y; if (!shown->pointleftbutton()) return 0; if ((lastx == shown->pointx())&&(lasty == shown->pointy())) return 0; lastx = shown->pointx(); lasty = shown->pointy(); xout = x = ((double)lastx)*scalex + lowx; yout = y = ((double)lasty)*scaley + lowy; *shown = *bg; paintcross(lastx,lasty); paintvalues(x,y); shown->reshow(); return 1; } /// Get current X value into xout. Returns 1 if there has /// been a change, 0 otherwise. int X(double &xout) { xout = x; if (!shown->pointleftbutton()) return 0; if (lastx == shown->pointx()) return 0; lastx = shown->pointx(); xout = x = ((double)lastx)*scalex + lowx; *shown = *bg; paintcross(lastx,lasty); paintvalues(x,y); shown->reshow(); return 1; } /// Get current Y value into yout. Returns 1 if there has /// been a change, 0 otherwise. int Y(double &yout) { yout = y; if (!shown->pointleftbutton()) return 0; if (lasty == shown->pointy()) return 0; lasty = shown->pointy(); yout = y = ((double)lasty)*scaley + lowy; *shown = *bg; paintcross(lastx,lasty); paintvalues(x,y); shown->reshow(); return 1; } }; /// \name Input and output of pictures as text //@{ /// Output monochrome picture as newline delimited rows of space /// delimited cols. template std::ostream& operator<<(std::ostream& os, picture_of_& pic) { for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) os << pic[i][j] << ' '; os << std::endl; } return os; } /// Input monochrome picture from text template std::istream& operator>>(std::istream& is, picture_of_& pic) { for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) is >> pic[i][j]; } return is; } /// Output colour picture as double-newline delimited planes of /// newline delimited rows of space delimited cols. template std::ostream& operator<<(std::ostream& os, colour_picture_of_& pic) { for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) os << pic.red[i][j] << ' '; os << std::endl; } os << std::endl; for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) os << pic.green[i][j] << ' '; os << std::endl; } os << std::endl; for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) os << pic.blue[i][j] << ' '; os << std::endl; } return os; } /// Input colour picture from text. template std::istream& operator>>(std::istream& is, colour_picture_of_& pic) { for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) is >> pic.red[i][j]; } for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) is >> pic.green[i][j]; } for(int i = 0; i < pic.nrows(); ++i) { for(int j = 0; j < pic.ncols(); ++j) is >> pic.blue[i][j]; } return is; } //@} /* CLIP_E */ // // New platform-independent display facilities via part of the CImg // library. See the copyright notice for the library below. // Modifications to this part of CImg.h are scattered throughout. // John Robinson. September 2004. // /*------------------------------------------------------------------------------------------------------ File : CImg.h Description : The C++ Template Image Processing Library Author : David Tschumperlďż˝ Institution : Initially developped at ODYSSEE Lab, INRIA Sophia Antipolis. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. ----------------------------------------------------------------------------------------------------*/ /* Detect and set CImg configuration flags If compilation flags are not adapted to your system, you can set them to correct values, before including the file CImg.h. -------------------------------------------------------------*/ } // namespace clip // Overcome VisualC++ 6.0 and DMC compilers namespace bug #if ( defined(_MSC_VER) && _MSC_VER<=1200 ) || defined(__DMC__) #define std #endif // Autodetection of the current OS #ifndef cimg_OS #if defined(NO_OS_SUPPORT) // Compiler flag to omit operating system support #define cimg_OS -1 #ifndef cimg_display_type #define cimg_display_type 0 #endif #elif defined(sun) || defined(__sun) // Solaris #define cimg_OS 0 #ifndef cimg_display_type #define cimg_display_type 1 #endif #elif defined(linux) || defined(__linux) || defined(__CYGWIN__) // Linux #define cimg_OS 1 #ifndef cimg_display_type #define cimg_display_type 1 #endif #ifndef cimg_color_terminal #define cimg_color_terminal #endif #elif defined(_WIN32) || defined(__WIN32__) // Windows #define cimg_OS 2 #ifndef cimg_display_type #define cimg_display_type 2 #endif #elif defined(__MACOSX__) || defined(__APPLE__) // Mac OS X #define cimg_OS 3 #ifndef cimg_display_type #define cimg_display_type 1 #endif #else // Other configurations #define cimg_OS -1 #ifndef cimg_display_type #define cimg_display_type 0 #endif #endif #endif // Debug flag. Set to 0 to remove dynamic debug messages. #ifndef cimg_debug #define cimg_debug 1 #endif // Architecture-dependent includes #if cimg_OS==-1 // Don't include anything #elif cimg_OS!=2 #include #include #else #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 // Overcome bug in macro definition in winuser.h (allow the use of TrackMouseEvent() ) #endif #include #endif #if cimg_display_type==1 #include #include #include namespace clip { namespace clip_internal { static pthread_mutex_t* cimg_mutex = NULL; static pthread_t* cimg_event_thread = NULL; static CImgDisplay* cimg_wins[1024]; static Display* cimg_displayX11 = NULL; static volatile unsigned int cimg_nb_wins = 0; static volatile bool cimg_thread_finished = false; static unsigned int cimg_bits = 0; static GC* cimg_gc = NULL; } // namespace clip_internal } // namespace clip #endif #ifdef min #undef min #undef max #undef abs #endif #if cimg_OS==0 || cimg_OS==1 // Keycodes for Unix & Solaris const unsigned int keyESC = 9; const unsigned int keyF1 = 67; const unsigned int keyF2 = 68; const unsigned int keyF3 = 69; const unsigned int keyF4 = 70; const unsigned int keyF5 = 71; const unsigned int keyF6 = 72; const unsigned int keyF7 = 73; const unsigned int keyF8 = 74; const unsigned int keyF9 = 75; const unsigned int keyF10 = 76; const unsigned int keyF11 = 95; const unsigned int keyF12 = 96; const unsigned int keyPAUSE = 110; const unsigned int key1 = 10; const unsigned int key2 = 11; const unsigned int key3 = 12; const unsigned int key4 = 13; const unsigned int key5 = 14; const unsigned int key6 = 15; const unsigned int key7 = 16; const unsigned int key8 = 17; const unsigned int key9 = 18; const unsigned int key0 = 19; const unsigned int keyBACKSPACE = 22; const unsigned int keyINSERT = 106; const unsigned int keyHOME = 97; const unsigned int keyPAGEUP = 99; const unsigned int keyTAB = 23; const unsigned int keyQ = 24; const unsigned int keyW = 25; const unsigned int keyE = 26; const unsigned int keyR = 27; const unsigned int keyT = 28; const unsigned int keyY = 29; const unsigned int keyU = 30; const unsigned int keyI = 31; const unsigned int keyO = 32; const unsigned int keyP = 33; const unsigned int keyDELETE = 107; const unsigned int keyEND = 103; const unsigned int keyPAGEDOWN = 105; const unsigned int keyCAPSLOCK = 66; const unsigned int keyA = 38; const unsigned int keyS = 39; const unsigned int keyD = 40; const unsigned int keyF = 41; const unsigned int keyG = 42; const unsigned int keyH = 43; const unsigned int keyJ = 44; const unsigned int keyK = 45; const unsigned int keyL = 46; const unsigned int keyENTER = 36; const unsigned int keySHIFTLEFT = 50; const unsigned int keyZ = 52; const unsigned int keyX = 53; const unsigned int keyC = 54; const unsigned int keyV = 55; const unsigned int keyB = 56; const unsigned int keyN = 57; const unsigned int keyM = 58; const unsigned int keySHIFTRIGHT = 62; const unsigned int keyARROWUP = 98; const unsigned int keyCTRLLEFT = 37; const unsigned int keyAPPLEFT = 115; const unsigned int keySPACE = 65; const unsigned int keyALTGR = 113; const unsigned int keyAPPRIGHT = 116; const unsigned int keyMENU = 117; const unsigned int keyCTRLRIGHT = 109; const unsigned int keyARROWLEFT = 100; const unsigned int keyARROWDOWN = 104; const unsigned int keyARROWRIGHT = 102; #elif cimg_OS==3 // Keycodes for MacOS X /* Well the following codes are those in the CImg package, but my Mac generates a different set, so those follow this comment const unsigned int keyESC = 61; const unsigned int keyF1 = 130; const unsigned int keyF2 = 128; const unsigned int keyF3 = 107; const unsigned int keyF4 = 126; const unsigned int keyF5 = 104; const unsigned int keyF6 = 105; const unsigned int keyF7 = 106; const unsigned int keyF8 = 108; const unsigned int keyF9 = 109; const unsigned int keyF10 = 117; const unsigned int keyF11 = 111; const unsigned int keyF12 = 119; const unsigned int keyPAUSE = 110; const unsigned int key1 = 26; const unsigned int key2 = 27; const unsigned int key3 = 28; const unsigned int key4 = 29; const unsigned int key5 = 31; const unsigned int key6 = 30; const unsigned int key7 = 34; const unsigned int key8 = 36; const unsigned int key9 = 33; const unsigned int key0 = 37; const unsigned int keyBACKSPACE = 59; const unsigned int keyINSERT = 122; const unsigned int keyHOME = 123; const unsigned int keyPAGEUP = 124; const unsigned int keyTAB = 56; const unsigned int keyA = 20; const unsigned int keyZ = 21; const unsigned int keyE = 22; const unsigned int keyR = 23; const unsigned int keyT = 25; const unsigned int keyY = 24; const unsigned int keyU = 40; const unsigned int keyI = 42; const unsigned int keyO = 39; const unsigned int keyP = 43; const unsigned int keyDELETE = 125; const unsigned int keyEND = 127; const unsigned int keyPAGEDOWN = 129; const unsigned int keyCAPSLOCK = 65; const unsigned int keyQ = 8; const unsigned int keyS = 9; const unsigned int keyD = 10; const unsigned int keyF = 11; const unsigned int keyG = 13; const unsigned int keyH = 12; const unsigned int keyJ = 46; const unsigned int keyK = 48; const unsigned int keyL = 45; const unsigned int keyM = 49; const unsigned int keyENTER = 44; const unsigned int keySHIFTLEFT = 64; const unsigned int keyW = 14; const unsigned int keyX = 15; const unsigned int keyC = 16; const unsigned int keyV = 17; const unsigned int keyB = 19; const unsigned int keyN = 53; const unsigned int keySHIFTRIGHT = 131; const unsigned int keyARROWUP = 70; const unsigned int keyCTRLLEFT = 62; const unsigned int keyAPPLEFT = 63; const unsigned int keySPACE = 57; const unsigned int keyALTGR = 132; const unsigned int keyAPPRIGHT = 134; const unsigned int keyMENU = 117; const unsigned int keyCTRLRIGHT = 133; const unsigned int keyARROWLEFT = 67; const unsigned int keyARROWDOWN = 69; const unsigned int keyARROWRIGHT = 68; */ // Following codes are for my Powermac. Your mileage may vary. const unsigned int keyESC = 61; const unsigned int keyF1 = 130; const unsigned int keyF2 = 128; const unsigned int keyF3 = 107; const unsigned int keyF4 = 126; const unsigned int keyF5 = 104; const unsigned int keyF6 = 105; const unsigned int keyF7 = 106; const unsigned int keyF8 = 108; /* Below this with the F keys seems to be reserved for the system const unsigned int keyF9 = const unsigned int keyF10 = const unsigned int keyF11 = const unsigned int keyF12 = const unsigned int keyF13 = const unsigned int keyF14 = const unsigned int keyF15 = const unsigned int keyF16 = */ const unsigned int keyPLUSMINUS = 18; const unsigned int key1 = 26; const unsigned int key2 = 27; const unsigned int key3 = 28; const unsigned int key4 = 29; const unsigned int key5 = 31; const unsigned int key6 = 30; const unsigned int key7 = 34; const unsigned int key8 = 36; const unsigned int key9 = 33; const unsigned int key0 = 37; const unsigned int keyMINUS = 35; const unsigned int keyEQUALS = 32; const unsigned int keyBACKSPACE = 59; const unsigned int keyHELP = 122; const unsigned int keyHOME = 123; const unsigned int keyPAGEUP = 124; const unsigned int keyTAB = 56; const unsigned int keyQ = 20; const unsigned int keyW = 21; const unsigned int keyE = 22; const unsigned int keyR = 23; const unsigned int keyT = 25; const unsigned int keyY = 24; const unsigned int keyU = 40; const unsigned int keyI = 42; const unsigned int keyO = 39; const unsigned int keyP = 43; const unsigned int keyOPENBRAC = 41; const unsigned int keyCLOSEBRAC = 38; const unsigned int keyENTER = 44; const unsigned int keyDELETE = 125; const unsigned int keyEND = 127; const unsigned int keyPAGEDOWN = 129; const unsigned int keyCAPSLOCK = 65; const unsigned int keyA = 8; const unsigned int keyS = 9; const unsigned int keyD = 10; const unsigned int keyF = 11; const unsigned int keyG = 13; const unsigned int keyH = 12; const unsigned int keyJ = 46; const unsigned int keyK = 48; const unsigned int keyL = 45; const unsigned int keyCOLON = 49; const unsigned int keyAPOSTROPHE = 47; const unsigned int keyBACKSLASH = 50; const unsigned int keySHIFTLEFT = 64; const unsigned int keyBACKQUOTE = 58; const unsigned int keyZ = 14; const unsigned int keyX = 15; const unsigned int keyC = 16; const unsigned int keyV = 17; const unsigned int keyB = 19; const unsigned int keyN = 53; const unsigned int keyM = 54; const unsigned int keyCOMMA = 51; const unsigned int keyFULLSTOP = 55; const unsigned int keySLASH = 52; const unsigned int keySHIFTRIGHT = 64; // Keeping same names as CImg uses I think here // Note that CTRLLEFT and CTRLRIGHT are same, as are // APPLEFT and APPRIGHT const unsigned int keyCTRLLEFT = 67; const unsigned int keyAPPLEFT = 63; const unsigned int keySPACE = 57; const unsigned int keyAPPRIGHT = 63; const unsigned int keyALTGR = 66; const unsigned int keyCTRLRIGHT = 67; const unsigned int keyARROWUP = 134; const unsigned int keyARROWLEFT = 131; const unsigned int keyARROWDOWN = 133; const unsigned int keyARROWRIGHT = 132; #else // Keycodes for Windows-OS // //@{ //!\name Keycodes. //! This list of keycodes is for MS Windows. //! Others are defined for Linux and the Mac. //! Compare return values from picture_of_::key() or //! colour_picture_of_::key() with these constants to //! interpret keystrokes. //! This list is derived from the CImg library. const unsigned int keyESC = 27; const unsigned int keyF1 = 112; const unsigned int keyF2 = 113; const unsigned int keyF3 = 114; const unsigned int keyF4 = 115; const unsigned int keyF5 = 116; const unsigned int keyF6 = 117; const unsigned int keyF7 = 118; const unsigned int keyF8 = 119; const unsigned int keyF9 = 120; const unsigned int keyF10 = 121; const unsigned int keyF11 = 122; const unsigned int keyF12 = 123; const unsigned int keyPAUSE = 19; const unsigned int key1 = 49; const unsigned int key2 = 50; const unsigned int key3 = 51; const unsigned int key4 = 52; const unsigned int key5 = 53; const unsigned int key6 = 54; const unsigned int key7 = 55; const unsigned int key8 = 56; const unsigned int key9 = 57; const unsigned int key0 = 48; const unsigned int keyBACKSPACE = 8; const unsigned int keyINSERT = 45; const unsigned int keyHOME = 36; const unsigned int keyPAGEUP = 33; const unsigned int keyTAB = 9; const unsigned int keyQ = 81; const unsigned int keyW = 87; const unsigned int keyE = 69; const unsigned int keyR = 82; const unsigned int keyT = 84; const unsigned int keyY = 89; const unsigned int keyU = 85; const unsigned int keyI = 73; const unsigned int keyO = 79; const unsigned int keyP = 80; const unsigned int keyDELETE = 8; const unsigned int keyEND = 35; const unsigned int keyPAGEDOWN = 34; const unsigned int keyCAPSLOCK = 20; const unsigned int keyA = 65; const unsigned int keyS = 83; const unsigned int keyD = 68; const unsigned int keyF = 70; const unsigned int keyG = 71; const unsigned int keyH = 72; const unsigned int keyJ = 74; const unsigned int keyK = 75; const unsigned int keyL = 76; const unsigned int keyENTER = 13; const unsigned int keySHIFTLEFT = 16; const unsigned int keyZ = 90; const unsigned int keyX = 88; const unsigned int keyC = 67; const unsigned int keyV = 86; const unsigned int keyB = 66; const unsigned int keyN = 78; const unsigned int keyM = 77; const unsigned int keySHIFTRIGHT = 16; const unsigned int keyARROWUP = 38; const unsigned int keyCTRLLEFT = 17; const unsigned int keyAPPLEFT = 91; const unsigned int keySPACE = 32; const unsigned int keyALTGR = 17; const unsigned int keyAPPRIGHT = 92; const unsigned int keyMENU = 93; const unsigned int keyCTRLRIGHT = 17; const unsigned int keyARROWLEFT = 37; const unsigned int keyARROWDOWN = 40; const unsigned int keyARROWRIGHT = 39; //@} #endif namespace clip { namespace clip_internal { // Because using picture_of_s instead of colour_picture_of_s, need a couple of macros: template inline PEL maxval_of_3(const picture_of_& a, const picture_of_& b, const picture_of_& c) { PEL am = a.max(); PEL bm = b.max(); PEL cm = c.max(); if (bm > am) am = bm; if (cm > am) am = cm; return am; } template inline PEL minval_of_3(const picture_of_& a, const picture_of_& b, const picture_of_& c) { PEL am = a.min(); PEL bm = b.min(); PEL cm = c.min(); if (bm < am) am = bm; if (cm < am) am = cm; return am; } // Get the value of the system timer with a millisecond precision. inline long int cimg_time() { #if cimg_OS==0 || cimg_OS==1 || cimg_OS==3 struct timeval st_time; gettimeofday(&st_time,NULL); return (long int)(st_time.tv_usec/1000 + st_time.tv_sec*1000); #elif cimg_OS==2 static SYSTEMTIME st_time; GetSystemTime(&st_time); return (long int)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); #else return 0; #endif } // Sleep for a certain numbers of milliseconds. inline void cimg_sleep(const int milliseconds) { #if cimg_OS==0 || cimg_OS==1 || cimg_OS==3 struct timespec tv; tv.tv_sec = milliseconds/1000; tv.tv_nsec = (milliseconds%1000)*1000000; nanosleep(&tv,NULL); #elif cimg_OS==2 Sleep(milliseconds); #endif } int cimg_wait(const int milliseconds) { static long int latest_time(cimg_time()); const long int current_time = cimg_time(); int time_diff = (int) (milliseconds + latest_time - current_time); if (time_diff>0) { latest_time = current_time + time_diff; cimg_sleep(time_diff); // Next line is because on Unixes nanosleep may not return for // another 20ms latest_time = cimg_time(); return (time_diff); } latest_time = current_time; return (time_diff); } /*------------------------------------------------------- Definition of the CImgDisplay structure ------------------------------------------------------*/ // This structure is used to display a CImg image in a window, and allows to catch mouse and keyboard events struct CImgDisplay { unsigned int width; //< Width of the display window. unsigned int height; //< Height of the display window. unsigned int normalize; //< Pixel value normalization. // = 0 for no normalization // = 1 for normalize to limits of current image // = 2 for normalize to limits of first image displayed // in this CImgDisplay // = 3 (new) for reshow_as_colour - taking values in // a single channel (mono) for all three colours const unsigned int attributes; //< Attributes of the display window. volatile int mousex; //< When (attributes&3)>=2, this variable corresponds to the mouse pointer X-coordinate into the display window. volatile int mousey; //< When (attributes&3)>=2, this variable corresponds to the mouse pointer Y-coordinate into the display window. volatile unsigned int button; //< When (attributes&3)>=2, these variables corresponds to the mouse button clicked into the display window. volatile unsigned int key; //< When (attributes&3)>=2, these variables corresponds to the key pressed into the display window. volatile bool closed; //< When (attributes&3)>=1, this variable tells if the window is closed (true) or still visible (false). volatile unsigned int resized; //< When (attributes&3)>=1, this variable tells if the window has been resized (nw<<16+nh). double min,max; int xat, yat; // The coordinates of the window for showat() const CImgDisplay *parent; // The parent for showat() // Return the width of the display window const int dimx() const { return (int)width; } // Return the height of the display window. const int dimy() const { return (int)height; } // Return the resized width of the display window (if resize event occured) const int resizedimx() const { return (int)(resized>>16); } // Return the resized height of the display window (if resize event occured) const int resizedimy() const { return (int)(resized&0xFFFF); } CImgDisplay& operator=(const CImgDisplay& disp) { fprintf(stderr,"CImgDisplay()::operator=() : Assignement of CImgDisplay is not allowed. Use pointers instead !"); return *this; } const CImgDisplay& wait(const unsigned int milliseconds) const { cimg_wait(milliseconds); return *this; } // When no display available //--------------------------- #if cimg_display_type==0 void nodisplay_available() { static bool first = true; if (first) { fprintf(stderr,"This program has called a picture display function but was compiled without display support.\n"); first = false; } } // Create a display window from an image. template CImgDisplay(const CImgDisplay *pparent, const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const char *title=NULL,const unsigned int pnormalize=1,const unsigned int pattributes=3,const int x_at = -1, const int y_at = -1): parent(pparent),normalize(pnormalize),attributes(pattributes),xat(x_at),yat(y_at),mousex(-1),mousey(-1),button(0),key(0),closed(pattributes&8?true:false),resized(0),min(0),max(0) { width = img.ncols(); height = img.nrows(); if (normalize==2) { min=minval_of_3(img,imr,imb); max=maxval_of_3(img,imr,imb); } nodisplay_available(); } // Resize a display window with new dimensions \p nwidth and \p nheight. CImgDisplay& resize(const int width, const int height,const bool redraw=true,const bool force=true) { return *this; } // Destructor. Close and destroy a display. ~CImgDisplay() {} // Fill the pixel data of the window buffer according to the image \p pimg. template void render(const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const unsigned int ymin=0,const unsigned int ymax=~0) {} // Display an image in a window. template CImgDisplay& display(const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const unsigned int ymin=0,const unsigned int ymax=-1) { return *this; } // Wait for a window event CImgDisplay& wait() { return *this; } // Show a closed display CImgDisplay& show() { return *this; } // Close a visible display CImgDisplay& close() { return *this; } // Move the window void moveto(const int x_at, const int y_at) {} // Clean up before deletion void cleanup() { } // Rename the window void rename(const char *comment) { } // X11-based display //------------------ #elif cimg_display_type==1 unsigned long *data; XImage *image; Window window; template CImgDisplay(const CImgDisplay *pparent, const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const char *title=NULL,const unsigned int pnormalize=1,const unsigned int pattributes=3,const int x_at = -1, const int y_at = -1): parent(pparent),normalize(pnormalize),attributes(pattributes),xat(x_at),yat(y_at),mousex(-1),mousey(-1),button(0),key(0),closed(pattributes&8?true:false),resized(0),min(0),max(0) { width = img.ncols(); height = img.nrows(); if (normalize==2) { min=minval_of_3(img,imr,imb); max=maxval_of_3(img,imr,imb); } new_lowlevel(title); display(img,imr,imb); } CImgDisplay& resize(const int nwidth, const int nheight,const bool redraw=true,const bool force=true) { const unsigned int dimx=nwidth>0?nwidth:-width*nwidth/100, dimy=nheight>0?nheight:-height*nheight/100; if (!dimx || !dimy) return *this; if (dimx==width && dimy==height) return *this; unsigned long *ndata = new unsigned long[dimx*dimy]; if (redraw) for (unsigned int y=0; ywindow,xat,yat,width,height,0,CopyFromParent,CopyFromParent,visual,xattributes_mask,&xattributes); else window = XCreateWindow(cimg_displayX11,RootWindow(cimg_displayX11,DefaultScreen(cimg_displayX11)),xat,yat,width,height,0,CopyFromParent,CopyFromParent,visual,xattributes_mask,&xattributes); } data = new unsigned long[width*height]; image = XCreateImage(cimg_displayX11,DefaultVisual(cimg_displayX11,DefaultScreen(cimg_displayX11)),cimg_bits,ZPixmap,0,(char*)data,width,height,8,0); XStoreName(cimg_displayX11,window,title?title:""); if (!closed) { XEvent event; XSelectInput(cimg_displayX11,window,StructureNotifyMask); XMapWindow(cimg_displayX11,window); do XWindowEvent(cimg_displayX11,window,StructureNotifyMask,&event); while (event.type!=MapNotify); } if (attributes&3) { Atom atom = XInternAtom(cimg_displayX11, "WM_DELETE_WINDOW", False); XSetWMProtocols(cimg_displayX11, window, &atom, 1); } cimg_wins[cimg_nb_wins++]=this; pthread_mutex_unlock(cimg_mutex); } void rename(const char *comment) { XStoreName(cimg_displayX11,window,comment); } // This is the callback void proc_lowlevel(XEvent *pevent) { const unsigned int buttoncode[3] = { 1,4,2 }; XEvent event=*pevent; switch (event.type) { case ClientMessage: XUnmapWindow(cimg_displayX11,window); closed=true; mousex=mousey=-1; key=button=0; break; case ResizeRequest: { while (XCheckWindowEvent(cimg_displayX11,window,ResizeRedirectMask,&event)); const unsigned int nw = event.xresizerequest.width, nh = event.xresizerequest.height; if (attributes&0x10 && nw && nh) resized=(nw<<16)|nh; else XResizeWindow(cimg_displayX11,window,width,height); } break; case Expose: while (XCheckWindowEvent(cimg_displayX11,window,ExposureMask,&event)); XPutImage(cimg_displayX11,window,*cimg_gc,image,0,0,0,0,width,height); break; case ButtonPress: while (XCheckWindowEvent(cimg_displayX11,window,ButtonPressMask,&event)); button |= buttoncode[event.xbutton.button-1]; mousex = event.xmotion.x; mousey = event.xmotion.y; if (mousex<0) mousex = 0; if (mousey<0) mousey = 0; if (mousex>=dimx()) mousex = width-1; if (mousey>=dimy()) mousey = height-1; break; case ButtonRelease: while (XCheckWindowEvent(cimg_displayX11,window,ButtonReleaseMask,&event)); button &= ~buttoncode[event.xbutton.button-1]; break; case KeyPress: while (XCheckWindowEvent(cimg_displayX11,window,KeyPressMask,&event)); key = event.xkey.keycode; break; case KeyRelease: while (XCheckWindowEvent(cimg_displayX11,window,KeyReleaseMask,&event)); key = 0; break; case LeaveNotify: while (XCheckWindowEvent(cimg_displayX11,window,LeaveWindowMask,&event)); /*mousex=mousey=-1; key=button=0;*/ break; case MotionNotify: while (XCheckWindowEvent(cimg_displayX11,window,PointerMotionMask,&event)); mousex = event.xmotion.x; mousey = event.xmotion.y; if (mousex<0) mousex = 0; if (mousey<0) mousey = 0; if (mousex>=dimx()) mousex = width-1; if (mousey>=dimy()) mousey = height-1; break; } } // This is the thread looking after the window static void* thread_lowlevel(void *arg) { XEvent event; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); for (;;) { pthread_mutex_lock(cimg_mutex); for (unsigned int i=0; iattributes)&3; const unsigned int emask = ((xevent_type>=1)?ExposureMask|StructureNotifyMask|ResizeRedirectMask:0) |((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0) |((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); XSelectInput(cimg_displayX11,cimg_wins[i]->window,emask); } bool event_flag = XCheckTypedEvent(cimg_displayX11, ClientMessage, &event); if (!event_flag) event_flag = XCheckMaskEvent(cimg_displayX11, ExposureMask|StructureNotifyMask|ResizeRedirectMask|ButtonPressMask |KeyPressMask|PointerMotionMask|LeaveWindowMask|ButtonReleaseMask|KeyReleaseMask,&event); if (event_flag) { for (unsigned int i=0; iclosed && event.xany.window==cimg_wins[i]->window) cimg_wins[i]->proc_lowlevel(&event); cimg_thread_finished = true; } pthread_mutex_unlock(cimg_mutex); cimg_wait(25); } return NULL; } template XImage* render(const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const unsigned int ymin=0,const unsigned int ymax=~0) { /* if (img.ncols()!=width || img.nrows()!=height) { colour_picture_of_ scaled(height,width,img.ismonochrome()); double xscale = ((double)width)/img.ncols(); double yscale = ((double)height)/img.nrows(); for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) scaled.map(j, i, img, (double)j/xscale, (double)i/yscale, 0); return render(scaled,0,~0); } */ // cerr << "render picture of size " << img.ncols() << " x " << img.nrows() << endl; // cerr << "normalize = " << normalize << ", cimg_bits = " << cimg_bits << endl; const bool by=(ymin<=ymax); const unsigned int nymin = by?ymin:ymax, nymax = by?(ymax>=height?height-1:ymax):(ymin>=height?height-1:ymin), w=width; pthread_mutex_lock(cimg_mutex); XImage *ximg = image; if (normalize == 3) { // Show as colour switch (cimg_bits) { case 16: for (unsigned int y=nymin; y<=nymax; y++) { PEL *data1 = img.rowptr(y); for (unsigned int x=0; x>(3+16)))<<11) | ((0x3f&(((int)*data1)>>(2+8)))<<5) | (0x1f&(((int)*data1)>>3))); data1++; } } break; case 24: for (unsigned int y=nymin; y<=nymax; y++) { PEL *data1 = img.rowptr(y); for (unsigned int x=0; x>3)<<11) | (((unsigned char)*(data2++)>>2)<<5) | ((unsigned char)*(data3++)>>3)); } } break; case 24: for (unsigned int y=nymin; y<=nymax; y++) { PEL *data1 = imr.rowptr(y); PEL *data2 = img.rowptr(y); PEL *data3 = imb.rowptr(y); for (unsigned int x=0; x>3)<<11) | ((val2>>2)<<5) | (val3>>3)); } } break; case 24: for (unsigned int y=nymin; y<=nymax; y++) { PEL *data1 = imr.rowptr(y); PEL *data2 = img.rowptr(y); PEL *data3 = imb.rowptr(y); for (unsigned int x=0; x CImgDisplay& display(const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const int renormalize = -2, const unsigned int pymin=0,const unsigned int pymax=~0) { // cerr << "display" << endl; if (renormalize >= 0) // Change normalize flag normalize = (unsigned int) renormalize; const unsigned int ymin = pymin=height?height-1:pymax):(pymin>=height?height-1:pymin); render(img,imr,imb,ymin,ymax); if (!closed) { pthread_mutex_lock(cimg_mutex); XPutImage(cimg_displayX11,window,*cimg_gc,image,0,ymin,0,ymin,width,ymax-ymin+1); XFlush(cimg_displayX11); pthread_mutex_unlock(cimg_mutex); } // cerr << "done display" << endl; return *this; } CImgDisplay& wait() { if (!closed && (attributes&3)) { XEvent event; do { pthread_mutex_lock(cimg_mutex); const unsigned int xevent_type = attributes&3; const unsigned int emask = ExposureMask|StructureNotifyMask|ResizeRedirectMask |((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0)|((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); XSelectInput(cimg_displayX11,window,emask); XPeekEvent(cimg_displayX11,&event); cimg_thread_finished = false; pthread_mutex_unlock(cimg_mutex); } while (event.xany.window!=window); while (!cimg_thread_finished) cimg_wait(25); } return *this; } CImgDisplay& show() { if (closed) { // cerr << "show requested" << endl; pthread_mutex_lock(cimg_mutex); XEvent event; XSelectInput(cimg_displayX11,window,StructureNotifyMask); XMapWindow(cimg_displayX11,window); do XWindowEvent(cimg_displayX11,window,StructureNotifyMask,&event); while (event.type!=MapNotify); XPutImage(cimg_displayX11,window,*cimg_gc,image,0,0,0,0,width,height); XFlush(cimg_displayX11); closed = false; pthread_mutex_unlock(cimg_mutex); } return *this; } CImgDisplay& close() { if (!closed) { // cerr << "close requested" << endl; pthread_mutex_lock(cimg_mutex); XUnmapWindow(cimg_displayX11,window); XFlush(cimg_displayX11); closed = true; pthread_mutex_unlock(cimg_mutex); } return *this; } // Windows-based display //----------------------- #elif cimg_display_type==2 CLIENTCREATESTRUCT ccs; BITMAPINFO bmi; unsigned int *data; DEVMODE curr_mode; HWND window; HDC hdc; HANDLE thread; HANDLE wait_disp; HANDLE created; HANDLE destroyed; HANDLE mutex; template CImgDisplay(const CImgDisplay *pparent, const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const char *title=NULL,const unsigned int pnormalize=1,const unsigned int pattributes=3,const int x_at = -1, const int y_at = -1): parent(pparent), normalize(pnormalize),attributes(pattributes),xat(x_at),yat(y_at),mousex(-1),mousey(-1),button(0),key(0),closed(pattributes&8?true:false),resized(0),min(0),max(0) { width = img.ncols(); height = img.nrows(); if (normalize==2) { min=minval_of_3(img,imr,imb); max = maxval_of_3(img,imr,imb); } new_lowlevel(title); display(img,imr,imb); } CImgDisplay& resize(const int nwidth, const int nheight,const bool redraw=true,const bool force=true) { const unsigned int dimx=nwidth>0?nwidth:(-nwidth)*width/100, dimy=nheight>0?nheight:(-nheight)*height/100; if (!dimx || !dimy) return *this; if (dimx==width && dimy==height) return *this; unsigned int *ndata = new unsigned int[dimx*dimy]; if (redraw) for (unsigned int y=0; ybestbpp) { bestbpp = mode.dmBitsPerPel; ibest=imode; } if(!bestbpp) fprintf(stderr,"CImgDisplay::new_lowlevel() : Could not initialize fullscreen mode %dx%d\n",width,height); if (bestbpp) { curr_mode.dmSize = sizeof(DEVMODE); curr_mode.dmDriverExtra = 0; EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&curr_mode); EnumDisplaySettings(NULL,ibest,&mode); ChangeDisplaySettings(&mode,0); } else curr_mode.dmSize = 0; } else curr_mode.dmSize = 0; if (attributes&3) { mutex = CreateMutex(NULL,FALSE,NULL); created = CreateEvent(NULL,FALSE,FALSE,NULL); destroyed = CreateEvent(NULL,FALSE,FALSE,NULL); wait_disp = CreateEvent(NULL,FALSE,FALSE,NULL); thread = CreateThread(NULL,0,thread_lowlevel,arg,0,&ThreadID); WaitForSingleObject(created,INFINITE); } else thread_lowlevel(arg); } static LRESULT APIENTRY proc_lowlevel(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { CImgDisplay* disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); const unsigned int event_type = disp->attributes&3; MSG st_msg; switch(msg) { case WM_CLOSE: disp->closed=true; ReleaseMutex(disp->mutex); ShowWindow(disp->window,SW_HIDE); return 0; case WM_SIZE: { while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)); WaitForSingleObject(disp->mutex,INFINITE); const unsigned int nw = LOWORD(lParam), nh = HIWORD(lParam); if (disp->attributes&0x10 && nw && nh) disp->resized=(nw<<16)|nh; else SetWindowPos(window,0,0,0,disp->width+9,disp->height+28,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); ReleaseMutex(disp->mutex); } break; case WM_PAINT: WaitForSingleObject(disp->mutex,INFINITE); SetDIBitsToDevice(disp->hdc,0,0,disp->width,disp->height,0,0,0,disp->height,disp->data,&(disp->bmi),DIB_RGB_COLORS); ReleaseMutex(disp->mutex); break; } if (event_type>=2) switch(msg) { case WM_KEYDOWN: while (PeekMessage(&st_msg,window,WM_KEYDOWN,WM_KEYDOWN,PM_REMOVE)); disp->key=(int)wParam; break; case WM_MOUSELEAVE: // disp->mousex=disp->mousey=-1; disp->button=disp->key=0; break; case WM_MOUSEMOVE: { while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)); disp->mousex = LOWORD(lParam); disp->mousey = HIWORD(lParam); /* if (disp->mousex<0) disp->mousex = 0; if (disp->mousey<0) disp->mousey = 0; if (disp->mousex>=(int)disp->width) disp->mousex = disp->width-1; if (disp->mousey>=(int)disp->height) disp->mousey = disp->height-1; */ TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = disp->window; TrackMouseEvent(&tme); } break; case WM_LBUTTONDOWN: while (PeekMessage(&st_msg,window,WM_LBUTTONDOWN,WM_LBUTTONDOWN,PM_REMOVE)); disp->button |= 1; break; case WM_RBUTTONDOWN: while (PeekMessage(&st_msg,window,WM_RBUTTONDOWN,WM_RBUTTONDOWN,PM_REMOVE)); disp->button |= 2; break; case WM_MBUTTONDOWN: while (PeekMessage(&st_msg,window,WM_MBUTTONDOWN,WM_MBUTTONDOWN,PM_REMOVE)); disp->button |= 4; break; } if (event_type>=3) switch(msg) { case WM_KEYUP: while (PeekMessage(&st_msg,window,WM_KEYUP,WM_KEYUP,PM_REMOVE)); disp->key=0; break; case WM_LBUTTONUP: while (PeekMessage(&st_msg,window,WM_LBUTTONUP,WM_LBUTTONUP,PM_REMOVE)); disp->button &= ~1; break; case WM_RBUTTONUP: while (PeekMessage(&st_msg,window,WM_RBUTTONUP,WM_RBUTTONUP,PM_REMOVE)); disp->button &= ~2; break; case WM_MBUTTONUP: while (PeekMessage(&st_msg,window,WM_MBUTTONUP,WM_MBUTTONUP,PM_REMOVE)); disp->button &= ~4; break; } return DefWindowProc(window,msg,wParam,lParam); } static DWORD WINAPI thread_lowlevel(void* arg) { CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]); const char *title = (const char*)(((void**)arg)[1]); MSG msg; delete[] (void**)arg; disp->bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); disp->bmi.bmiHeader.biWidth=disp->width; disp->bmi.bmiHeader.biHeight=-(int)disp->height; disp->bmi.bmiHeader.biPlanes=1; disp->bmi.bmiHeader.biBitCount=32; disp->bmi.bmiHeader.biCompression=BI_RGB; disp->bmi.bmiHeader.biSizeImage=0; disp->bmi.bmiHeader.biXPelsPerMeter=1; disp->bmi.bmiHeader.biYPelsPerMeter=1; disp->bmi.bmiHeader.biClrUsed=0; disp->bmi.bmiHeader.biClrImportant=0; disp->data = new unsigned int[disp->width*disp->height]; if (!disp->curr_mode.dmSize) { if (disp->xat >= 0) { //showat() case if (disp->parent){ disp->window = CreateWindow("MDICLIENT",title?title:"", WS_CHILD | WS_VISIBLE, disp->xat,disp->yat, disp->width,disp->height,disp->parent->window,NULL,NULL,&(disp->ccs)); } else disp->window = CreateWindow("MDICLIENT",title?title:"", WS_POPUP | WS_VISIBLE, disp->xat,disp->yat, disp->width,disp->height,NULL,NULL,NULL,&(disp->ccs)); // WS_POPUP is the thing that takes off the decorations } else { int cwidth,cheight; RECT rect; rect.left=rect.top=0; rect.right=disp->width-1; rect.bottom=disp->height-1; if (AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false)) { cwidth = rect.right-rect.left+1; cheight = rect.bottom-rect.top+1; } else { cwidth = disp->width+9; cheight = disp->height+28; } disp->window = CreateWindow("MDICLIENT",title?title:"", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,CW_USEDEFAULT, cwidth,cheight,NULL,NULL,NULL,&(disp->ccs)); } } else disp->window = CreateWindow("MDICLIENT",title?title:"", WS_POPUP | WS_VISIBLE, CW_USEDEFAULT,CW_USEDEFAULT, disp->width,disp->height,NULL,NULL,NULL,&(disp->ccs)); SetForegroundWindow(disp->window); disp->hdc = GetDC(disp->window); if (disp->attributes&3) { SetClassLong(disp->window,GCL_HBRBACKGROUND,NULL); // Look after my own background painting SetWindowLong(disp->window,GWL_USERDATA,(LONG)disp); SetWindowLong(disp->window,GWL_WNDPROC,(LONG)proc_lowlevel); SetEvent(disp->created); while( GetMessage( &msg, NULL, 0, 0 ) > 0) { DispatchMessage( &msg ); SetEvent(disp->wait_disp); } } SetEvent(disp->destroyed); return 0; } void rename(const char *comment) { SetWindowText(window,comment); } template BITMAPINFO* render(const picture_of_& img, const picture_of_& imr, const picture_of_& imb,const unsigned int ymin=0,const unsigned int ymax=~0) { /* if (img.width!=width || img.height!=height) return render(img.get_resize(width,height,1,-100,1),0,~0); */ const bool by=(ymin<=ymax); const unsigned int nymin = by?ymin:ymax, nymax = by?(ymax>=height?height-1:ymax):(ymin>=height?height-1:ymin), w=width; unsigned int *ximg = data + nymin*width; WaitForSingleObject(mutex,INFINITE); if (normalize == 3) // Show as colour for (unsigned int y=nymin; y<=nymax; y++) { PEL *data1 = img.rowptr(y); for (unsigned int x=0; xmoveto(Nx,Ny); return reshow(normalize); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) { cimgparent = parent->qxp; } qxp = new CImgDisplay(cimgparent,*this,*this,*this, "CLIP image", normalize,3,Nx,Ny); return 0; } template int picture_of_::showat(const int Nx, const int Ny, const bool normalize, const colour_picture_of_ *parent) { // cerr << "showat(" << Nx << "," << Ny << "," << normalize << ")\n"; using clip_internal::CImgDisplay; if (qxp) { qxp->moveto(Nx,Ny); return reshow(normalize); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) cimgparent = parent->qxp; qxp = new CImgDisplay(cimgparent,*this,*this,*this, "CLIP image", normalize,3,Nx,Ny); return 0; } template int picture_of_::showascolour(const char *comment) { using clip_internal::CImgDisplay; if (qxp) return reshowascolour(comment); qxp = new CImgDisplay((const CImgDisplay *)0,*this,*this,*this, comment, 3); return 0; } template int picture_of_::showascolourat(const int Nx, const int Ny, const picture_of_ *parent) { using clip_internal::CImgDisplay; if (qxp) { qxp->moveto(Nx,Ny); return reshowascolour(); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) cimgparent = parent->qxp; qxp = new CImgDisplay(cimgparent,*this,*this,*this, "CLIP image", 3,3,Nx,Ny); return 0; } template int picture_of_::showascolourat(const int Nx, const int Ny, const colour_picture_of_ *parent) { using clip_internal::CImgDisplay; if (qxp) { qxp->moveto(Nx,Ny); return reshowascolour(); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) cimgparent = parent->qxp; qxp = new CImgDisplay(cimgparent,*this,*this,*this, "CLIP image", 3,3,Nx,Ny); return 0; } template int picture_of_::reshowascolour() { using clip_internal::CImgDisplay; if (!qxp) return -1; qxp->display(*this,*this,*this,3); return 0; } template int picture_of_::reshowascolour(const char *comment) { using clip_internal::CImgDisplay; if (!qxp) return -1; qxp->rename(comment); qxp->display(*this,*this,*this,3); return 0; } template int picture_of_::reshow(const bool normalize) { if (!qxp) return -1; qxp->display(*this,*this,*this,normalize); return 0; } template int picture_of_::reshow(const char *comment, const bool normalize) { if (!qxp) return -1; qxp->rename(comment); qxp->display(*this,*this,*this,normalize); return 0; } template int picture_of_::unshow() { if (!qxp) return -1; qxp->cleanup(); delete qxp; qxp = 0; return 0; } template int picture_of_::rename(const char *comment) { if (!qxp) return -1; qxp->rename(comment); return 0; } template int picture_of_::pointx() { if (!qxp) return -1; return(qxp->mousex); } template int picture_of_::pointy() { if (!qxp) return -1; return(qxp->mousey); } template int picture_of_::pointleftbutton() { if (!qxp) return -1; return(qxp->button & 1); } template int picture_of_::pointmiddlebutton() { if (!qxp) return -1; return(qxp->button & 4); } template int picture_of_::pointrightbutton() { if (!qxp) return -1; return(qxp->button & 2); } template int picture_of_::key() { if (!qxp) return -1; return(qxp->key); } template int picture_of_::clearkey() { if (!qxp) return -1; qxp->key = 0; return(0); } template int picture_of_::pointshiftbutton() { if (!qxp) return -1; return((qxp->button)&&(qxp->key)); } template int picture_of_::closerequested() { if (!qxp) return -1; return(qxp->closed); } template int colour_picture_of_::show(const char *comment, const bool normalize) { using clip_internal::CImgDisplay; if (qxp) return reshow(comment,normalize); qxp = new CImgDisplay((const CImgDisplay *)0,this->green,this->red,this->blue, comment, normalize); return 0; } template int colour_picture_of_::showat(const int Nx, const int Ny, const bool normalize, const picture_of_ *parent) { using clip_internal::CImgDisplay; if (qxp) { qxp->moveto(Nx,Ny); return reshow(normalize); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) cimgparent = parent->qxp; qxp = new CImgDisplay(cimgparent,this->green,this->red,this->blue, "CLIP image", normalize,3,Nx,Ny); return 0; } template int colour_picture_of_::showat(const int Nx, const int Ny, const bool normalize, const colour_picture_of_ *parent) { using clip_internal::CImgDisplay; if (qxp) { qxp->moveto(Nx,Ny); return reshow(normalize); } CImgDisplay *cimgparent = 0; if (parent&&parent->qxp) cimgparent = parent->qxp; qxp = new CImgDisplay(cimgparent,this->green,this->red,this->blue, "CLIP image", normalize,3,Nx,Ny); return 0; } template int colour_picture_of_::reshow(const bool normalize) { if (!qxp) return -1; qxp->display(this->green,this->red,this->blue,normalize); return 0; } template int colour_picture_of_::reshow(const char *comment, const bool normalize) { if (!qxp) return -1; qxp->rename(comment); qxp->display(this->green,this->red,this->blue,normalize); return 0; } template int colour_picture_of_::fastreshow() { // Old version for normalize 0 if (!qxp) return -1; qxp->display(this->green,this->red,this->blue,0); return 0; } template int colour_picture_of_::fastreshow(const char *comment) { if (!qxp) return -1; qxp->rename(comment); qxp->display(this->green,this->red,this->blue,0); return 0; } template int colour_picture_of_::unshow() { if (!qxp) return -1; qxp->cleanup(); delete qxp; qxp = 0; return 0; } template int colour_picture_of_::rename(const char *comment) { if (!qxp) return -1; qxp->rename(comment); return 0; } template int colour_picture_of_::pointx() { if (!qxp) return -1; return(qxp->mousex); } template int colour_picture_of_::pointy() { if (!qxp) return -1; return(qxp->mousey); } template int colour_picture_of_::pointleftbutton() { if (!qxp) return -1; return(qxp->button & 1); } template int colour_picture_of_::pointmiddlebutton() { if (!qxp) return -1; qxp->wait(10); return(qxp->button & 4); } template int colour_picture_of_::pointrightbutton() { if (!qxp) return -1; return(qxp->button & 2); } template int colour_picture_of_::key() { if (!qxp) return -1; return(qxp->key); } template int colour_picture_of_::clearkey() { if (!qxp) return -1; qxp->key = 0; return(0); } template int colour_picture_of_::pointshiftbutton() { if (!qxp) return -1; return((qxp->button)&&(qxp->key)); // At present returning true if any key is pressed along with button } template int colour_picture_of_::closerequested() { if (!qxp) return -1; return(qxp->closed); } /* CLIP_F */ namespace clip_internal { // // Multiplatform support for webcams // // First some utility functions for colour conversion common to all platforms // The following color conversion function is from the ov511 module #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) template static inline void move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, PEL *pg, PEL *pr, PEL *pb, PEL *pgplus, PEL *prplus, PEL *pbplus) { const int bvScale = 91881; const int guScale = -22553; const int gvScale = -46801; const int ruScale = 116129; const int yScale = 65536; int r, g, b; g = guScale * u + gvScale * v; r = ruScale * u; b = bvScale * v; yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; /* Write out top two pixels */ *pr++ = (PEL) LIMIT(b+yTL); *pg++ = (PEL) LIMIT(g+yTL); *pb++ = (PEL) LIMIT(r+yTL); *pr = (PEL) LIMIT(b+yTR); *pg = (PEL) LIMIT(g+yTR); *pb = (PEL) LIMIT(r+yTR); /* Skip down to next line to write out bottom two pixels */ *prplus++ = (PEL) LIMIT(b+yBL); *pgplus++ = (PEL) LIMIT(g+yBL); *pbplus++ = (PEL) LIMIT(r+yBL); *prplus = (PEL) LIMIT(b+yBR); *pgplus = (PEL) LIMIT(g+yBR); *pbplus = (PEL) LIMIT(r+yBR); } template void colour_convert(PEL **g, PEL **r, PEL **b, const int height, const int width, const unsigned char *in) { // Converts from planar YUV420 to RGB24. const int numpix = width * height; int y00, y01, y10, y11, u, v; const unsigned char *pY = in; const unsigned char *pU = pY + numpix; const unsigned char *pV = pU + numpix / 4; PEL *gg, *rr, *bb, *ggp, *rrp, *bbp; for (int j = 0; j <= height - 2; j += 2) { gg = g[j]; rr = r[j]; bb = b[j]; ggp = g[j+1]; rrp = r[j+1]; bbp = b[j+1]; for (int i = 0; i <= width - 2; i += 2) { y00 = *pY; y01 = *(pY + 1); y10 = *(pY + width); y11 = *(pY + width + 1); u = (*pU++) - 128; v = (*pV++) - 128; move_420_block(y00, y01, y10, y11, u, v, width, gg, rr, bb, ggp, rrp, bbp); pY += 2; gg += 2; rr += 2; bb += 2; ggp += 2; rrp += 2; bbp += 2; } pY += width; } } template void transfer_data(const unsigned char *src, PEL **r, PEL **g, PEL **b, const int campic_width, const int campic_height, const int pic_type, const bool toucam) { if (toucam) { // Philips TouCam if (pic_type == 1) { for (int i = 0; i < campic_height; i++) for (int j = 0; j < campic_width; j++) g[i][j] = (PEL) *src++; } else { colour_convert(g,r,b,campic_height,campic_width, src); } } else { // Ordinary. Assume RGB // For some reason, at least on quickcams, it appears we // have to reverse the ordering from bottom to top. // However, I'm going to stick with the normal ordering // because you need this for vicams. if (pic_type == 1) { for (int i = 0; i < campic_height; i++) // for (int i = campic_height-1; i >= 0; i--) for (int j = 0; j < campic_width; j++) { // NOTE: DIVISIONS DONE UP FRONT NOW IN CASE PEL is uchar // HAVE TO MAKE SIMILAR CHANGE TO LINUX CASE g[i][j] = (PEL) (*src++>>2); g[i][j] += (PEL) (*src++>>1); // Twice weight on green g[i][j] += (PEL) (*src++>>2); } } else { for (int i = 0; i < campic_height; i++) // for (int i = campic_height-1; i >= 0; i--) for (int j = 0; j < campic_width; j++) { b[i][j] = (PEL) *src++; g[i][j] = (PEL) *src++; r[i][j] = (PEL) *src++; } } } } #if (defined(linux) || defined(__linux)) && !defined(NO_OS_SUPPORT) // camdriver // Linux Framegrabber class for CLIP // Adapted October 2003, Sept 2004 by John Robinson, from ... /*************************************************************************** testcam.cpp - description ------------------- begin : Wed Aug 14 2002 copyright : (C) 2002 by Enrico Costanza email : e.costanza@ieee.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ // // Here is pwc-ioctl.h for Philips webcams // #ifndef PWC_IOCTL_H #define PWC_IOCTL_H /* (C) 2001-2002 Nemosoft Unv. webcam@smcc.demon.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This is pwc-ioctl.h belonging to PWC 8.6 */ /* Changes 2001/08/03 Alvarado Added ioctl constants to access methods for changing white balance and red/blue gains */ /* These are private ioctl() commands, specific for the Philips webcams. They contain functions not found in other webcams, and settings not specified in the Video4Linux API. The #define names are built up like follows: VIDIOC VIDeo IOCtl prefix PWC Philps WebCam G optional: Get S optional: Set ... the function */ /* The frame rate is encoded in the video_window.flags parameter using the upper 16 bits, since some flags are defined nowadays. The following defines provide a mask and shift to filter out this value. In 'Snapshot' mode the camera freezes its automatic exposure and colour balance controls. */ #define PWC_FPS_SHIFT 16 #define PWC_FPS_MASK 0x00FF0000 #define PWC_FPS_FRMASK 0x003F0000 #define PWC_FPS_SNAPSHOT 0x00400000 struct pwc_probe { char name[32]; int type; }; /* pwc_whitebalance.mode values */ #define PWC_WB_INDOOR 0 #define PWC_WB_OUTDOOR 1 #define PWC_WB_FL 2 #define PWC_WB_MANUAL 3 #define PWC_WB_AUTO 4 /* Used with VIDIOCPWC[SG]AWB (Auto White Balance). Set mode to one of the PWC_WB_* values above. *red and *blue are the respective gains of these colour components inside the camera; range 0..65535 When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; otherwise undefined. 'read_red' and 'read_blue' are read-only. */ struct pwc_whitebalance { int mode; int manual_red, manual_blue; /* R/W */ int read_red, read_blue; /* R/O */ }; /* 'control_speed' and 'control_delay' are used in automatic whitebalance mode, and tell the camera how fast it should react to changes in lighting, and with how much delay. Valid values are 0..65535. */ struct pwc_wb_speed { int control_speed; int control_delay; }; /* Used with VIDIOCPWC[SG]LED */ struct pwc_leds { int led_on; /* Led on-time; range = 0..25000 */ int led_off; /* Led off-time; range = 0..25000 */ }; /* Restore user settings */ #define VIDIOCPWCRUSER _IO('v', 192) /* Save user settings */ #define VIDIOCPWCSUSER _IO('v', 193) /* Restore factory settings */ #define VIDIOCPWCFACTORY _IO('v', 194) /* You can manipulate the compression factor. A compression preference of 0 means use uncompressed modes when available; 1 is low compression, 2 is medium and 3 is high compression preferred. Of course, the higher the compression, the lower the bandwidth used but more chance of artefacts in the image. The driver automatically chooses a higher compression when the preferred mode is not available. */ /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ #define VIDIOCPWCSCQUAL _IOW('v', 195, int) /* Get preferred compression quality */ #define VIDIOCPWCGCQUAL _IOR('v', 195, int) /* This is a probe function; since so many devices are supported, it becomes difficult to include all the names in programs that want to check for the enhanced Philips stuff. So in stead, try this PROBE; it returns a structure with the original name, and the corresponding Philips type. To use, fill the structure with zeroes, call PROBE and if that succeeds, compare the name with that returned from VIDIOCGCAP; they should be the same. If so, you can be assured it is a Philips (OEM) cam and the type is valid. */ #define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ #define VIDIOCPWCSAGC _IOW('v', 200, int) /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ #define VIDIOCPWCGAGC _IOR('v', 200, int) /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ #define VIDIOCPWCSSHUTTER _IOW('v', 201, int) /* Color compensation (Auto White Balance) */ #define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) #define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) /* Auto WB speed */ #define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) #define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) /* LEDs on/off/blink; int range 0..65535 */ #define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) #define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ #define VIDIOCPWCSCONTOUR _IOW('v', 206, int) #define VIDIOCPWCGCONTOUR _IOR('v', 206, int) /* Backlight compensation; 0 = off, otherwise on */ #define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) #define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) /* Flickerless mode; = 0 off, otherwise on */ #define VIDIOCPWCSFLICKER _IOW('v', 208, int) #define VIDIOCPWCGFLICKER _IOR('v', 208, int) /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ #define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) #define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) #endif // Use the following for videodev.h rather than the one in // /usr/include/linux because that has conflicts with the iostream // files on SuSE 8.2. #ifndef __LINUX_VIDEODEV_H #define __LINUX_VIDEODEV_H #include #include #ifndef __BIT_TYPES_DEFINED__ #define __BIT_TYPES_DEFINED__ typedef __u8 u_int8_t; typedef __s8 int8_t; typedef __u16 u_int16_t; typedef __s16 int16_t; typedef __u32 u_int32_t; typedef __s32 int32_t; #endif /* !(__BIT_TYPES_DEFINED__) */ // Following commented out because of conflict on SuSE Linux 9.1 // typedef __u8 unsigned int8_t; // typedef __u16 unsigned int16_t; // typedef __u32 unsigned int32_t; #if defined(__GNUC__) && !defined(__STRICT_ANSI__) // typedef __u64 unsigned int64_t; typedef __u64 u_int64_t; typedef __s64 int64_t; #endif #include #if 0 /* * v4l2 is still work-in-progress, integration planed for 2.5.x * v4l2 project homepage: http://www.thedirks.org/v4l2/ * patches available from: http://bytesex.org/patches/ */ # define HAVE_V4L2 1 # include #else # undef HAVE_V4L2 #endif #ifdef __KERNEL__ #include #include struct video_device { struct module *owner; char name[32]; int type; /* v4l1 */ int type2; /* v4l2 */ int hardware; int minor; /* old, obsolete interface -- dropped in 2.5.x, don't use it */ int (*open)(struct video_device *, int mode); void (*close)(struct video_device *); long (*read)(struct video_device *, char *, unsigned long, int noblock); long (*write)(struct video_device *, const char *, unsigned long, int noblock); unsigned int (*poll)(struct video_device *, struct file *, poll_table *); int (*ioctl)(struct video_device *, unsigned int , void *); int (*mmap)(struct video_device *, const char *, unsigned long); int (*initialize)(struct video_device *); /* new interface -- we will use file_operations directly * like soundcore does. */ struct file_operations *fops; void *priv; /* Used to be 'private' but that upsets C++ */ /* for videodev.c intenal usage -- don't touch */ int users; struct semaphore lock; devfs_handle_t devfs_handle; }; #define VIDEO_MAJOR 81 extern int video_register_device(struct video_device *, int type, int nr); #define VFL_TYPE_GRABBER 0 #define VFL_TYPE_VBI 1 #define VFL_TYPE_RADIO 2 #define VFL_TYPE_VTX 3 extern void video_unregister_device(struct video_device *); extern struct video_device* video_devdata(struct file*); extern int video_exclusive_open(struct inode *inode, struct file *file); extern int video_exclusive_release(struct inode *inode, struct file *file); extern int video_usercopy(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int (*func)(struct inode *inode, struct file *file, unsigned int cmd, void *arg)); #endif /* __KERNEL__ */ #define VID_TYPE_CAPTURE 1 /* Can capture */ #define VID_TYPE_TUNER 2 /* Can tune */ #define VID_TYPE_TELETEXT 4 /* Does teletext */ #define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ #define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ #define VID_TYPE_CLIPPING 32 /* Can clip */ #define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ #define VID_TYPE_SCALES 128 /* Scalable */ #define VID_TYPE_MONOCHROME 256 /* Monochrome only */ #define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ #define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ #define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ #define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ #define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ struct video_capability { char name[32]; int type; int channels; /* Num channels */ int audios; /* Num audio devices */ int maxwidth; /* Supported width */ int maxheight; /* And height */ int minwidth; /* Supported width */ int minheight; /* And height */ }; struct video_channel { int channel; char name[32]; int tuners; __u32 flags; #define VIDEO_VC_TUNER 1 /* Channel has a tuner */ #define VIDEO_VC_AUDIO 2 /* Channel has audio */ __u16 type; #define VIDEO_TYPE_TV 1 #define VIDEO_TYPE_CAMERA 2 __u16 norm; /* Norm set by channel */ }; struct video_tuner { int tuner; char name[32]; unsigned long rangelow, rangehigh; /* Tuner range */ __u32 flags; #define VIDEO_TUNER_PAL 1 #define VIDEO_TUNER_NTSC 2 #define VIDEO_TUNER_SECAM 4 #define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ #define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ #define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ #define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ #define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ __u16 mode; /* PAL/NTSC/SECAM/OTHER */ #define VIDEO_MODE_PAL 0 #define VIDEO_MODE_NTSC 1 #define VIDEO_MODE_SECAM 2 #define VIDEO_MODE_AUTO 3 __u16 signal; /* Signal strength 16bit scale */ }; struct video_picture { __u16 brightness; __u16 hue; __u16 colour; __u16 contrast; __u16 whiteness; /* Black and white only */ __u16 depth; /* Capture depth */ __u16 palette; /* Palette in use */ #define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ #define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ #define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ #define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ #define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ #define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ #define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ #define VIDEO_PALETTE_YUYV 8 #define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ #define VIDEO_PALETTE_YUV420 10 #define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ #define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ #define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ #define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ #define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ #define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ #define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ #define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ }; struct video_audio { int audio; /* Audio channel */ __u16 volume; /* If settable */ __u16 bass, treble; __u32 flags; #define VIDEO_AUDIO_MUTE 1 #define VIDEO_AUDIO_MUTABLE 2 #define VIDEO_AUDIO_VOLUME 4 #define VIDEO_AUDIO_BASS 8 #define VIDEO_AUDIO_TREBLE 16 #define VIDEO_AUDIO_BALANCE 32 char name[16]; #define VIDEO_SOUND_MONO 1 #define VIDEO_SOUND_STEREO 2 #define VIDEO_SOUND_LANG1 4 #define VIDEO_SOUND_LANG2 8 __u16 mode; __u16 balance; /* Stereo balance */ __u16 step; /* Step actual volume uses */ }; struct video_clip { __s32 x,y; __s32 width, height; struct video_clip *next; /* For user use/driver use only */ }; struct video_window { __u32 x,y; /* Position of window */ __u32 width,height; /* Its size */ __u32 chromakey; __u32 flags; struct video_clip *clips; /* Set only */ int clipcount; #define VIDEO_WINDOW_INTERLACE 1 #define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ #define VIDEO_CLIP_BITMAP -1 /* bitmap is 1024x625, a '1' bit represents a clipped pixel */ #define VIDEO_CLIPMAP_SIZE (128 * 625) }; struct video_capture { __u32 x,y; /* Offsets into image */ __u32 width, height; /* Area to capture */ __u16 decimation; /* Decimation divider */ __u16 flags; /* Flags for capture */ #define VIDEO_CAPTURE_ODD 0 /* Temporal */ #define VIDEO_CAPTURE_EVEN 1 }; struct video_buffer { void *base; int height,width; int depth; int bytesperline; }; struct video_mmap { unsigned int frame; /* Frame (0 - n) for double buffer */ int height,width; unsigned int format; /* should be VIDEO_PALETTE_* */ }; struct video_key { __u8 key[8]; __u32 flags; }; #define VIDEO_MAX_FRAME 32 struct video_mbuf { int size; /* Total memory to map */ int frames; /* Frames */ int offsets[VIDEO_MAX_FRAME]; }; #define VIDEO_NO_UNIT (-1) struct video_unit { int video; /* Video minor */ int vbi; /* VBI minor */ int radio; /* Radio minor */ int audio; /* Audio minor */ int teletext; /* Teletext minor */ }; struct vbi_format { __u32 sampling_rate; /* in Hz */ __u32 samples_per_line; __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ __s32 start[2]; /* starting line for each frame */ __u32 count[2]; /* count of lines for each frame */ __u32 flags; #define VBI_UNSYNC 1 /* can distingues between top/bottom field */ #define VBI_INTERLACED 2 /* lines are interlaced */ }; /* video_info is biased towards hardware mpeg encode/decode */ /* but it could apply generically to any hardware compressor/decompressor */ struct video_info { __u32 frame_count; /* frames output since decode/encode began */ __u32 h_size; /* current unscaled horizontal size */ __u32 v_size; /* current unscaled veritcal size */ __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ __u32 picture_type; /* current picture type */ __u32 temporal_reference; /* current temporal reference */ __u8 user_data[256]; /* user data last found in compressed stream */ /* user_data[0] contains user data flags, user_data[1] has count */ }; /* generic structure for setting playback modes */ struct video_play_mode { int mode; int p1; int p2; }; /* for loading microcode / fpga programming */ struct video_code { char loadwhat[16]; /* name or tag of file being passed */ int datasize; __u8 *data; }; #define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ #define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ #define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ #define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ #define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ #define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ #define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ #define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ #define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ #define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ #define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ #define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ #define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ #define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ #define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ #define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ #define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ #define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ #define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ #define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ #define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ #define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ #define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ #define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ #define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ #define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ #define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ #define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ #define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ #define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ /* VIDIOCSWRITEMODE */ #define VID_WRITE_MPEG_AUD 0 #define VID_WRITE_MPEG_VID 1 #define VID_WRITE_OSD 2 #define VID_WRITE_TTX 3 #define VID_WRITE_CC 4 #define VID_WRITE_MJPEG 5 /* VIDIOCSPLAYMODE */ #define VID_PLAY_VID_OUT_MODE 0 /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ #define VID_PLAY_GENLOCK 1 /* p1: 0 = OFF, 1 = ON */ /* p2: GENLOCK FINE DELAY value */ #define VID_PLAY_NORMAL 2 #define VID_PLAY_PAUSE 3 #define VID_PLAY_SINGLE_FRAME 4 #define VID_PLAY_FAST_FORWARD 5 #define VID_PLAY_SLOW_MOTION 6 #define VID_PLAY_IMMEDIATE_NORMAL 7 #define VID_PLAY_SWITCH_CHANNELS 8 #define VID_PLAY_FREEZE_FRAME 9 #define VID_PLAY_STILL_MODE 10 #define VID_PLAY_MASTER_MODE 11 /* p1: see below */ #define VID_PLAY_MASTER_NONE 1 #define VID_PLAY_MASTER_VIDEO 2 #define VID_PLAY_MASTER_AUDIO 3 #define VID_PLAY_ACTIVE_SCANLINES 12 /* p1 = first active; p2 = last active */ #define VID_PLAY_RESET 13 #define VID_PLAY_END_MARK 14 #define VID_HARDWARE_BT848 1 #define VID_HARDWARE_QCAM_BW 2 #define VID_HARDWARE_PMS 3 #define VID_HARDWARE_QCAM_C 4 #define VID_HARDWARE_PSEUDO 5 #define VID_HARDWARE_SAA5249 6 #define VID_HARDWARE_AZTECH 7 #define VID_HARDWARE_SF16MI 8 #define VID_HARDWARE_RTRACK 9 #define VID_HARDWARE_ZOLTRIX 10 #define VID_HARDWARE_SAA7146 11 #define VID_HARDWARE_VIDEUM 12 /* Reserved for Winnov videum */ #define VID_HARDWARE_RTRACK2 13 #define VID_HARDWARE_PERMEDIA2 14 /* Reserved for Permedia2 */ #define VID_HARDWARE_RIVA128 15 /* Reserved for RIVA 128 */ #define VID_HARDWARE_PLANB 16 /* PowerMac motherboard video-in */ #define VID_HARDWARE_BROADWAY 17 /* Broadway project */ #define VID_HARDWARE_GEMTEK 18 #define VID_HARDWARE_TYPHOON 19 #define VID_HARDWARE_VINO 20 /* SGI Indy Vino */ #define VID_HARDWARE_CADET 21 /* Cadet radio */ #define VID_HARDWARE_TRUST 22 /* Trust FM Radio */ #define VID_HARDWARE_TERRATEC 23 /* TerraTec ActiveRadio */ #define VID_HARDWARE_CPIA 24 #define VID_HARDWARE_ZR36120 25 /* Zoran ZR36120/ZR36125 */ #define VID_HARDWARE_ZR36067 26 /* Zoran ZR36067/36060 */ #define VID_HARDWARE_OV511 27 #define VID_HARDWARE_ZR356700 28 /* Zoran 36700 series */ #define VID_HARDWARE_W9966 29 #define VID_HARDWARE_SE401 30 /* SE401 USB webcams */ #define VID_HARDWARE_PWC 31 /* Philips webcams */ #define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */ #define VID_HARDWARE_CPIA2 33 #endif /* __LINUX_VIDEODEV_H */ /* * Local variables: * c-basic-offset: 8 * End: */ #include #include #include #include template class camdriver; static class camdriver *fg = 0; template class camdriver //: public SigC::Object { int camera; struct video_mbuf mBuf; int campic_width; int campic_height; unsigned char *grabBuffer; unsigned char *bigBuffer; PEL **green, **red, **blue; int initCamera( void ); int frameCount; int pic_type; int frame; int nextFrame; int driver; struct video_mmap vMmap; public: int enabled; camdriver(int in_width, int in_height, int driver); ~camdriver(); void request_frame(PEL **g, PEL **r, PEL **b); int frame_delivered(); unsigned char *grab(bool andRequestNextFrame); unsigned char *colourgrab(bool andRequestNextFrame); int setBrightness( int b ); int setHue( int h ); int setColour( int c ); int setContrast( int c ); int setWhiteness( int w ); //Phlilips Webcam Specific Properties int getCompression(); int setCompression( const int value ); int getNoiseReduction(); int setNoiseReduction( const int value ); int getAGC(); int setAGC( const int value ); int getShutter(); int setShutter( const int value ); int setFPS( int data ); int getContour(); int setContour( const int value ); bool getBacklightAuto(); bool setBacklightAuto( const bool value ); bool getAntiFlicker(); bool setAntiFlicker( const bool value ); void toggleBacklightAuto(); void toggleAntiFlicker(); int getWhiteBalanceMode(); int setWhiteBalanceMode( const int value ); int getWhiteBalanceRed(); int setWhiteBalanceRed( const int value ); int getWhiteBalanceBlue(); int setWhiteBalanceBlue( const int value ); void resetPwc(); }; template camdriver::camdriver(int in_width, int in_height, int in_driver) : campic_width(in_width), campic_height(in_height) , driver(in_driver) { enabled = 0; frame = 0; nextFrame = 1; if (initCamera() < 0) return; // Failed // resetPwc(); vMmap.height = campic_height; vMmap.width = campic_width; vMmap.frame = 0; //request capture of frame 0 if(ioctl(camera, VIDIOCMCAPTURE, &vMmap)<0) { cout << "error in VIDIOCMCAPTURE, frame 0!" << endl; return; } enabled = 1; // Assume that the camera works if we got this far! vMmap.frame = 1; bigBuffer = (unsigned char *) mmap(0, mBuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, camera, 0); // And collect the first frame ioctl(camera, VIDIOCSYNC, &frame); grabBuffer = bigBuffer + mBuf.offsets[frame]; // frame++; // frame %= mBuf.frames; } template camdriver::~camdriver() { if (enabled) munmap(bigBuffer, mBuf.size); } template int camdriver::initCamera( ) { char device[] = "/dev/videoX"; device[10] = driver + '0'; camera = open(device, O_RDWR+O_NONBLOCK ); if( camera<0 ) { cerr << "Error: cannot open " << device << endl; return -1; } // cout << "camera: " << camera << endl; struct video_capability vCap; if( ioctl(camera, VIDIOCGCAP, &vCap) < 0 ) { cout << "Error: cannot get device capabilities(ioctl)" << endl; return -1; } else { // cout << "camera properties:\n"; // cout << "name: " << vCap.name << endl; // cout << "type: " << vCap.type << endl; // cout << "channels: " << vCap.channels << endl; // cout << "audios: " << vCap.audios << endl; // cout << "maxwidth: " << vCap.maxwidth << endl; // cout << "maxheight: " << vCap.maxheight << endl; // cout << "minwidth: " << vCap.minwidth << endl; // cout << "minheight: " << vCap.minheight << endl; } struct video_window vWin; if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return -1; } else { // cout << "x: " << vWin.x << endl; // cout << "y: " << vWin.y << endl; // cout << "width: " << vWin.width << endl; // cout << "height: " << vWin.height << endl; // cout << "flags: " << vWin.flags << endl; } vWin.x = 0; vWin.y = 0; vWin.width = campic_width; vWin.height = campic_height; vWin.clipcount = 0; vWin.flags &= ~PWC_FPS_FRMASK; if (vWin.width > 320) vWin.flags |= ( 15 << PWC_FPS_SHIFT ); else vWin.flags |= ( 30 << PWC_FPS_SHIFT ); if( ioctl(camera, VIDIOCSWIN, &vWin) < 0 ) { cout << "Error: cannot set capture window(ioctl)" << endl; return -1; } else { // cout << "camera capture window set as follows" << endl; if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window(ioctl)" << endl; return -1; } // cout << "x: " << vWin.x << endl; // cout << "y: " << vWin.y << endl; // cout << "width: " << vWin.width << endl; // cout << "height: " << vWin.height << endl; // cout << "flags: " << vWin.flags << endl << endl; } struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } // vPic.brightness = 32768; // vPic.contrast = 32768; // vPic.hue = 32768; // vPic.colour = 32768; // cout << "palette is " << vPic.palette << endl; vMmap.format = vPic.palette = VIDEO_PALETTE_YUV420P; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Cannot set picture properties for preferred compression" << endl; cout << "Trying RGB instead" << endl; vMmap.format = vPic.palette = VIDEO_PALETTE_RGB24; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "That didn't work either." << endl; return -1; } } // cout << "image properties set as follows" << endl; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } // cout << "brightness: " << vPic.brightness << endl; // cout << "hue: " << vPic.hue << endl; // cout << "colour: " << vPic.colour << endl; // cout << "contrast: " << vPic.contrast << endl; // cout << "depth: " << vPic.depth << endl; // cout << "palette: "; // if( vPic.palette == VIDEO_PALETTE_YUV420P ) { // cout << "ok(YUV 4:2:0 Planar)" << endl; // } // else if( vPic.palette == VIDEO_PALETTE_RGB24 ) { // cout << "ok(RGB)" << endl; // } // cout << "whiteness: " << vPic.whiteness << endl << endl; // mBuf is defined as a class variable if( ioctl(camera, VIDIOCGMBUF, &mBuf) < 0 ) { cout << "Error: cannot get capture buffer properties(ioctl)" << endl; return -1; } else { // cout << "size: " << mBuf.size << endl; // cout << "frames: " << mBuf.frames << endl; // cout << "offsets: " << mBuf.offsets << endl; } if( mBuf.frames<2 ) { cout << "this device does not support double buffering!" << endl; return 0; } ioctl(camera, VIDIOCGMBUF, &mBuf); return 0; } template void camdriver::request_frame(PEL **g, PEL **r, PEL **b) { green = g; red = r; blue = b; if (green == red) pic_type = 1; else pic_type = 3; nextFrame = frame + 1; nextFrame %= mBuf.frames; vMmap.frame = nextFrame; //request the acquisition of next frame if(ioctl(camera, VIDIOCMCAPTURE, &vMmap)<0) { cout << "error in VIDIOCMCAPTURE, frame " << nextFrame << endl; struct video_window vWin; if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return; } else { cout << "x: " << vWin.x << endl; cout << "y: " << vWin.y << endl; cout << "width: " << vWin.width << endl; cout << "height: " << vWin.height << endl; cout << "flags: " << vWin.flags << endl; } return; } return; } template int camdriver::frame_delivered() { // For now this hangs until the frame is available ioctl(camera, VIDIOCSYNC, &frame); grabBuffer = bigBuffer + mBuf.offsets[frame]; camdriver *fgp = (camdriver *) fg; transfer_data(grabBuffer,fgp->red,fgp->green,fgp->blue, fgp->campic_width,fgp->campic_height,fgp->pic_type, ( fgp->vMmap.format == VIDEO_PALETTE_YUV420P )); frame++; frame %= mBuf.frames; return 1; } //******************************************************** //******************************************************** //******************************************************** //******************************************************** //******************************************************** template int camdriver::setBrightness( int b ) { struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)\n"; return -1; } vPic.brightness = b; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } else { //get properties to verify setting if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } } return vPic.brightness; } template int camdriver::setHue( int h ) { struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } vPic.hue = h; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } else { //get properties to verify setting if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } } return vPic.hue; } template int camdriver::setColour( int c ) { struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } vPic.colour = c; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } else { //get properties to verify setting if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } } return vPic.colour; } template int camdriver::setContrast( int c ) { struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } vPic.contrast = c; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } else { //get properties to verify setting if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } } return vPic.contrast; } template int camdriver::setWhiteness( int w ) { struct video_picture vPic; if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } vPic.whiteness = w; if( ioctl(camera, VIDIOCSPICT, &vPic) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } else { //get properties to verify setting if( ioctl(camera, VIDIOCGPICT, &vPic) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } } return vPic.whiteness; } //Philips Specific Properties template int camdriver::getCompression() { int data; if( ioctl(camera, VIDIOCPWCGCQUAL, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } return data; } template int camdriver::setCompression( const int value ) { int data = value; if( ioctl(camera, VIDIOCPWCSCQUAL, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } return getCompression(); } template int camdriver::getAGC() { int data; if( ioctl(camera, VIDIOCPWCGAGC, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl)" << endl; return -1; } return data; } template int camdriver::setAGC( const int value ) { int data = value; if( ioctl(camera, VIDIOCPWCSAGC, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl)" << endl; return -1; } return getAGC(); } template int camdriver::getShutter() { struct video_window vWin; if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return -1; } int fps = (vWin.flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; // cout << "fps: " << fps << endl; return fps; } template int camdriver::setFPS( int data ) { struct video_window vWin; if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return -1; } vWin.flags &= ~PWC_FPS_FRMASK; vWin.flags |= ( data << PWC_FPS_SHIFT ); int flag = ioctl(camera, VIDIOCSWIN, &vWin); /*if( flag == -EINVAL ) { cout << "invalid framerate" << endl; } else*/ if( flag<0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return -1; } if( ioctl(camera, VIDIOCGWIN, &vWin) < 0 ) { cout << "Error: cannot get capture window settings(ioctl)" << endl; return -1; } int fps = (vWin.flags & PWC_FPS_FRMASK) << PWC_FPS_SHIFT; cout << "fps: " << fps << endl; return fps; } template int camdriver::setShutter( const int value ) { int data = value; if( ioctl(camera, VIDIOCPWCSSHUTTER, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl setShutter)" << endl; return -1; } return getShutter(); } template int camdriver::getContour() { int data; if( ioctl(camera, VIDIOCPWCGCONTOUR, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl getContour)" << endl; return -1; } return data; } template int camdriver::setContour( const int value ) { int data = value; if( ioctl(camera, VIDIOCPWCSCONTOUR, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl setContour)" << endl; return -1; } return getContour(); } template bool camdriver::getBacklightAuto() { int data; if( ioctl(camera, VIDIOCPWCGBACKLIGHT, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl getBacklightAuto)" << endl; return -1; } if( data == 0 ) return false; //else return true; } template bool camdriver::setBacklightAuto( const bool value ) { int data = (int) value; if( ioctl(camera, VIDIOCPWCSBACKLIGHT, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl setBacklightAuto)" << endl; return false; } return getBacklightAuto(); } template void camdriver::toggleBacklightAuto() { if( getBacklightAuto() ) setBacklightAuto( false ); else setBacklightAuto( true ); } template bool camdriver::getAntiFlicker() { int data; if( ioctl(camera, VIDIOCPWCGFLICKER, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl getAntiFlicker)" << endl; return false; } if( data == 0 ) return false; //else return true; } template bool camdriver::setAntiFlicker( const bool value ) { int data = (int) value; if( ioctl(camera, VIDIOCPWCSFLICKER, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl setAntiFlicker)" << endl; return false; } return getAntiFlicker(); } template void camdriver::toggleAntiFlicker() { if( getAntiFlicker() ) setAntiFlicker( false ); else setAntiFlicker( true ); } template int camdriver::getNoiseReduction() { int data; if( ioctl(camera, VIDIOCPWCGDYNNOISE, &data) < 0 ) { cout << "Error: cannot get picture properties(ioctl getNoiseReduction)" << endl; return -1; } return data; } template int camdriver::setNoiseReduction( const int value ) { int data = value; if( ioctl(camera, VIDIOCPWCSDYNNOISE, &data) < 0 ) { cout << "Error: cannot set picture properties(ioctl setNoiseReduction)" << endl; return -1; } return getNoiseReduction(); } template int camdriver::getWhiteBalanceMode() { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl getWhiteBalanceMode)" << endl; return -1; } return wb.mode; } template int camdriver::setWhiteBalanceMode( const int value ) { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl setWhiteBalanceMode)" << endl; return -1; } wb.mode = value; if( ioctl(camera, VIDIOCPWCSAWB, &wb) < 0 ) { cout << "Error: cannot set picture properties(ioctl setWhiteBalanceMode)" << endl; return -1; } return getWhiteBalanceMode(); } template int camdriver::getWhiteBalanceRed() { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl getWhiteBalanceRed)" << endl; return -1; } if( wb.mode == PWC_WB_MANUAL ) return wb.manual_red; if( wb.mode == PWC_WB_AUTO ) return wb.read_red; return -1; } template int camdriver::setWhiteBalanceRed( const int value ) { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl setWhiteBalanceRed)" << endl; return -1; } wb.manual_red = value; if( ioctl(camera, VIDIOCPWCSAWB, &wb) < 0 ) { cout << "Error: cannot set picture properties(ioctl setWhiteBalanceRed)" << endl; return -1; } return getWhiteBalanceRed(); } template int camdriver::getWhiteBalanceBlue() { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl getWhiteBalanceBlue)" << endl; return -1; } if( wb.mode == PWC_WB_MANUAL ) return wb.manual_blue; if( wb.mode == PWC_WB_AUTO ) return wb.read_blue; return -1; } template int camdriver::setWhiteBalanceBlue( const int value ) { struct pwc_whitebalance wb; if( ioctl(camera, VIDIOCPWCGAWB, &wb) < 0 ) { cout << "Error: cannot get picture properties(ioctl setWhiteBalanceBlue)" << endl; return -1; } wb.manual_blue = value; if( ioctl(camera, VIDIOCPWCSAWB, &wb) < 0 ) { cout << "Error: cannot set picture properties(ioctl setWhiteBalanceBlue)" << endl; return -1; } return getWhiteBalanceBlue(); } template void camdriver::resetPwc() { if( ioctl(camera, VIDIOCPWCFACTORY) < 0 ) { cout << "Error: cannot set picture properties(ioctl resetPwc)" << endl; return; } return; } #elif (defined(_WIN32) || defined(__WIN32__)) && !defined(NO_OS_SUPPORT) // camdriver // Windows Framegrabber class for CLIP // Adapted from: // FrameGrabber.h: interface for the CFrameGrabber class. // Justen Hyde & Dan Parnham // Media Engineering Group // University York // 2004 // // Also uses portions of CImg for thread control ////////////////////////////////////////////////////////////////////// } // namespace clip_internal } // namespace clip // #include #include #pragma comment(lib, "vfw32.lib") #pragma comment(lib, "winmm.lib") namespace clip { namespace clip_internal { template class camdriver; static class camdriver *fg = 0; LRESULT PASCAL char_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); LRESULT PASCAL uchar_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); LRESULT PASCAL int_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); LRESULT PASCAL uint_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); LRESULT PASCAL float_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); LRESULT PASCAL double_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr); template class camdriver { // To get round difficulty with a template class for the callback, have to have // a set of explicit functions static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, char_FrameCallback);} static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, uchar_FrameCallback);} static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, int_FrameCallback);} static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, uint_FrameCallback);} static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, float_FrameCallback);} static inline void install_callback(camdriver *disp){capSetCallbackOnFrame(disp->window, double_FrameCallback);} // Private functions adapted from CImg and OpenIllusionist static DWORD WINAPI thread_lowlevel(void* arg) { camdriver *disp = (camdriver *)(((void**)arg)[0]); delete[] (void**)arg; disp->window = capCreateCaptureWindow("", WS_DISABLED, 0, 0, 1, 1, 0, 0); if (disp->window) { // Try given driver index, and if that doesn't work, go up // through others until we reach 9 while (!capDriverConnect(disp->window, disp->driver)) { cerr << "Can't connect camera driver " << disp->driver << endl; if (++disp->driver == 10) break; } if (disp->driver == 10) { disp->driver = 0; disp->enabled = false; } else { // capSetCallbackOnFrame(disp->window, FrameCallback); // capSetCallbackOnFrame(disp->window, FrameCallback); // cerr << "Connected camera driver " << disp->driver << endl; install_callback(disp); capPreview(disp->window, FALSE); capGetVideoFormat(disp->window, &disp->videoformat, sizeof(BITMAPINFO)); disp->videoformat.bmiHeader.biSizeImage = 0; disp->videoformat.bmiHeader.biWidth = disp->campic_width; disp->videoformat.bmiHeader.biHeight = disp->campic_height; // cerr << "orig planes = " << disp->videoformat.bmiHeader.biPlanes << endl; // cerr << "orig bitcount = " << disp->videoformat.bmiHeader.biBitCount << endl; // cerr << "orig compression = " << disp->videoformat.bmiHeader.biCompression << endl; // int preferred_compression = ('V'<<24) | ('U'<<16) | ('Y'<<8) | ('I'); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); char *compname = (char *) &disp->videoformat.bmiHeader.biCompression; // cerr << "compression as string = " << compname[0] << compname[1]; // cerr << compname[2] << compname[3] << endl; if (disp->videoformat.bmiHeader.biCompression != preferred_compression) { // cerr << "Camera compression not equal to preferred" << endl; // cerr << "Will try to set..." << endl; disp->videoformat.bmiHeader.biBitCount = 12; disp->videoformat.bmiHeader.biCompression = preferred_compression; if (!capSetVideoFormat(disp->window, &disp->videoformat, sizeof(BITMAPINFO))) { // cerr << "Attempt to set failed. Will use no compression." << endl; disp->videoformat.bmiHeader.biBitCount = 24; disp->videoformat.bmiHeader.biCompression = BI_RGB; } else { // cerr << "camera now returned compression = " << compname[0] << compname[1]; // cerr << compname[2] << compname[3] << endl; if (disp->videoformat.bmiHeader.biCompression != preferred_compression) { // cerr << "Will use no compression" << endl; disp->videoformat.bmiHeader.biBitCount = 24; disp->videoformat.bmiHeader.biCompression = BI_RGB; } } } if (!capSetVideoFormat(disp->window, &disp->videoformat, sizeof(BITMAPINFO))) { cerr << "Failed to set video format." << endl; disp->enabled = false; } } } else disp->enabled = false; SetEvent(disp->created); if (disp->enabled) { while (disp->last_grab_requested >= 0){ if (disp->last_grab_requested != disp->last_grab_delivered) { // cerr << "Grabbing" << endl; capGrabFrameNoStop(disp->window); } else WaitForSingleObject(disp->wait_disp,INFINITE); } } capSetCallbackOnFrame(disp->window, NULL); capDriverDisconnect(disp->window); DestroyWindow(disp->window); SetEvent(disp->destroyed); return 0; } void new_lowlevel() { unsigned long ThreadID; unsigned int imode=0,ibest=0,bestbpp=0; bool stopflag = false; void *arg = (void*)(new void*[2]); ((void**)arg)[0]=(void*)this; ((void**)arg)[1]=(void*)0; mutex = CreateMutex(NULL,FALSE,NULL); created = CreateEvent(NULL,FALSE,FALSE,NULL); destroyed = CreateEvent(NULL,FALSE,FALSE,NULL); wait_disp = CreateEvent(NULL,FALSE,FALSE,NULL); thread = CreateThread(NULL,0,thread_lowlevel,arg,0,&ThreadID); WaitForSingleObject(created,INFINITE); } public: int last_grab_requested; int last_grab_delivered; int pic_type; int campic_height; int campic_width; int driver; bool enabled; PEL **red, **green, **blue; HANDLE mutex; HANDLE wait_disp; HWND window; BITMAPINFO videoformat; HANDLE thread; HANDLE created; HANDLE destroyed; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// camdriver(int width = 320, int height = 240, int dr = 0) { if (fg) { // Panic: Frame grabber already exists cerr << "FrameGrabber already exists" << endl; return; } last_grab_requested = 0; last_grab_delivered = 0; fg = (camdriver *) this; enabled = true; mutex = CreateMutex(NULL,FALSE,NULL); campic_width = width; campic_height = height; driver = dr; new_lowlevel(); if (!enabled) { cerr << "Could not enable camera driver - probably no camera" << endl; } } ~camdriver() { if (enabled) { last_grab_requested = -1; // Signal to grab thread to die SetEvent(wait_disp); WaitForSingleObject(destroyed,INFINITE); } fg = 0; } void request_frame(PEL **g, PEL **r, PEL **b) { if (!enabled) return; green = g; red = r; blue = b; if (green == red) // Monochrome picture pic_type = 1; else pic_type = 3; WaitForSingleObject(mutex,INFINITE); last_grab_requested++; ReleaseMutex(mutex); SetEvent(wait_disp); } int frame_delivered() { int retval = -1; if (enabled) { retval = 0; camdriver *fgtemp = (camdriver *)fg; WaitForSingleObject(fgtemp->mutex,INFINITE); if (last_grab_requested == last_grab_delivered) { retval = 1; } SetEvent(wait_disp); // Probably not necessary: Ensures we don't get deadlocked in a race. ReleaseMutex(fgtemp->mutex); } return retval; } int ShowSettingsDialog() { // This needs work to get the dialog boxes up. At present they // don't appear when called and the program hangs. Is this because // the capture window is disabled? Don't think so. Tried it with // the window as a WS_POPUP | WS_CAPTION and a call to ShowWindow; // the capture window appeared but still couldn't display dialog // boxes. if (enabled) { // capDlgVideoSource(window); // capDlgVideoFormat(window); // capDlgVideoDisplay(window); return 1; } return 0; } // Now the functions provided in the Linux version that aren't supported // in video for windows int setShutter(const int value) { return 0; } int setAGC(const int value) { return 0; } }; // Now replicated functions to get around template callback problem // template LRESULT PASCAL char_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } LRESULT PASCAL uchar_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } LRESULT PASCAL int_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } LRESULT PASCAL uint_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } LRESULT PASCAL float_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } LRESULT PASCAL double_FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) { camdriver *fgtemp = (camdriver *)fg; if (!fgtemp) return -1; if (!fgtemp->enabled) return -1; WaitForSingleObject(fgtemp->mutex,INFINITE); int preferred_compression = ('0' << 24) | ('2'<<16) | ('4'<<8) | ('I'); transfer_data(lpVHdr->lpData,fgtemp->red,fgtemp->green,fgtemp->blue, fgtemp->campic_width,fgtemp->campic_height,fgtemp->pic_type, (fgtemp->videoformat.bmiHeader.biCompression == preferred_compression)); fgtemp->last_grab_delivered = fgtemp->last_grab_requested; ReleaseMutex(fgtemp->mutex); return 0; } #elif (defined(MACOS__) || defined(__APPLE__)) && !defined(NO_OS_SUPPORT) // Replace this when we have a driver // camdriver // Unknown O/S Framegrabber class for CLIP template class camdriver; static class camdriver *fg = 0; template class camdriver { public: int enabled; camdriver(int width = 320, int height = 240, int dr = 0) { enabled = 0; cerr << "Camera support not available for this operating system" << endl; } ~camdriver() { } void request_frame(PEL **g, PEL **r, PEL **b) { } int frame_delivered() { return -1; } int setShutter(const int value) { return 0; } int setAGC(const int value) { return 0; } }; #else template class camdriver; static class camdriver *fg = 0; template class camdriver { public: int enabled; camdriver(int width = 320, int height = 240, int dr = 0) { enabled = 0; cerr << "Camera support not available for this operating system" << endl; } ~camdriver() { } void request_frame(PEL **g, PEL **r, PEL **b) { } int frame_delivered() { return -1; } }; #endif #ifdef max #undef max #undef min #undef abs #endif // Calculate appropriate camera picture parameters static int pic_params(const int inx, const int iny, int& outx, int& outy) { if ((inx < 160)||(iny < 120)) { cerr << "Error: picture too small to attach a camera\n"; return -1; } if ((inx < 320)||(iny < 240)) { outx = 160; outy = 120; return 0; } if ((inx < 640)||(iny < 480)) { outx = 320; outy = 240; return 0; } outx = 640; outy = 480; return 0; } // Local pictures for double buffering static picture_of_ *pcurr = 0; static colour_picture_of_ *cpcurr = 0; } // namespace clip_internal template int picture_of_::attach_camera() { using namespace clip_internal; if (fg) { cerr << "Error: attempt to attach camera to a second picture\n"; return -1; } int pic_width, pic_height; if (pic_params(pr->cols,pr->rows,pic_width,pic_height) < 0) return -1; camdriver *fgtemp; fgtemp = new camdriver(pic_width,pic_height,0); if (!fgtemp) return -1; if (!fgtemp->enabled) { delete fgtemp; fg = 0; return -1; } fg = (camdriver *) fgtemp; pcurr = (picture_of_ *) new picture_of_(pr->rows,pr->cols,pr->border); // cout << "showsettings returned " << fg->ShowSettingsDialog() << endl; return 1; } template int picture_of_::request_frame() { using namespace clip_internal; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; picture_of_ *temp = (picture_of_ *) pcurr; fgtemp->request_frame(temp->bufptr(),temp->bufptr(),temp->bufptr()); return 0; } template int picture_of_::request_frame(int shutterspeed, int gain) { using namespace clip_internal; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; picture_of_ *temp = (picture_of_ *) pcurr; fgtemp->setAGC(gain); fgtemp->setShutter(shutterspeed); fgtemp->request_frame(temp->bufptr(),temp->bufptr(),temp->bufptr()); return 0; } template int picture_of_::frame_delivered() { using namespace clip_internal; int retval; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; if((retval = fgtemp->frame_delivered()) < 0) return -1; if (retval == 0) // Nothing yet return 0; picture_of_ *temp = (picture_of_ *) pcurr; // Update double buffering pic_rep *prtemp = temp->pr; temp->pr = pr; pr = prtemp; return 1; } template int picture_of_::detach_camera() { using namespace clip_internal; if (!fg) return -1; delete fg; delete pcurr; fg = 0; return 0; } template int colour_picture_of_::attach_camera() { using namespace clip_internal; if (fg) { cerr << "Error: attempt to attach camera to a second picture\n"; return -1; } int pic_width, pic_height; if (pic_params(mono.pr->cols,mono.pr->rows,pic_width,pic_height) < 0) return -1; camdriver *fgtemp; fgtemp = new camdriver(pic_width,pic_height,0); if (!fgtemp) return -1; if (!fgtemp->enabled) { delete fgtemp; fg = 0; return -1; } fg = (camdriver *) fgtemp; cpcurr = (colour_picture_of_ *) new colour_picture_of_(mono.pr->rows,mono.pr->cols,mono.pr->border,puremono); // cout << "showsettings returned " << fg->ShowSettingsDialog() << endl; // cpcurr->show("Backup picture"); return 1; } template int colour_picture_of_::request_frame() { using namespace clip_internal; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; colour_picture_of_ *temp = (colour_picture_of_ *) cpcurr; fgtemp->request_frame(temp->green.bufptr(),temp->red.bufptr(), temp->blue.bufptr()); return 0; } template int colour_picture_of_::request_frame(int shutterspeed, int gain) { using namespace clip_internal; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; fgtemp->setAGC(gain); fgtemp->setShutter(shutterspeed); colour_picture_of_ *temp = (colour_picture_of_ *) cpcurr; fgtemp->request_frame(temp->green.bufptr(),temp->red.bufptr(), temp->blue.bufptr()); return 0; } template int colour_picture_of_::frame_delivered() { using namespace clip_internal; int retval; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; if((retval = fgtemp->frame_delivered()) < 0) return -1; if (retval == 0) // Nothing yet return 0; // Update double buffering colour_picture_of_ *temp = (colour_picture_of_ *) cpcurr; if (puremono) { pic_rep *prtemp = temp->mono.pr; temp->mono.pr = mono.pr; mono.pr = prtemp; return 0; } pic_rep *prtemp = temp->red.pr; temp->red.pr = red.pr; red.pr = prtemp; prtemp = temp->green.pr; temp->green.pr = green.pr; green.pr = prtemp; prtemp = temp->blue.pr; temp->blue.pr = blue.pr; blue.pr = prtemp; // cpcurr->reshow(); return 1; } template int colour_picture_of_::detach_camera() { using namespace clip_internal; if (!fg) return -1; camdriver *fgtemp = (camdriver *) fg; delete fgtemp; delete cpcurr; fg = 0; return 0; } /* CLIP_G */ namespace clip_internal { // // Consolidated header file from Small JPEG Decoder Library by // Rich Geldreich // // //------------------------------------------------------------------------------ // main.h // Last updated: Nov. 16, 2000 // Copyright (C) 1994-2000 Rich Geldreich // richgel@voicenet.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ #ifndef MAIN_H #define MAIN_H typedef signed char schar; /* 8 bits */ typedef signed short int16; /* 16 bits */ typedef unsigned short uint16; /* 16 bits */ typedef signed int int32; /* 32+ bits */ #if defined(_WIN32) || defined(__WIN32__) // In these operating systems, min and max are defined as macros so these // would have been undef-ed above. template T inline min(T a, T b) { return (a < b) ? a : b; } template T inline max(T a, T b) { return (a > b) ? a : b; } #endif #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #endif //------------------------------------------------------------------------------ // jpegdecoder.h // Small JPEG Decoder Library v0.93b // Last updated: Dec. 28, 2001 // Copyright (C) 1994-2000 Rich Geldreich // richgel@voicenet.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ #ifndef JPEG_DECODER_H #define JPEG_DECODER_H //------------------------------------------------------------------------------ #define JPGD_INBUFSIZE 4096 //------------------------------------------------------------------------------ // May need to be adjusted if support for other colorspaces/sampling factors is added #define JPGD_MAXBLOCKSPERMCU 10 //------------------------------------------------------------------------------ #define JPGD_MAXHUFFTABLES 8 #define JPGD_MAXQUANTTABLES 4 #define JPGD_MAXCOMPONENTS 4 #define JPGD_MAXCOMPSINSCAN 4 //------------------------------------------------------------------------------ // Increase this if you increase the max width! #define JPGD_MAXBLOCKSPERROW 6144 //------------------------------------------------------------------------------ // Max. allocated blocks #define JPGD_MAXBLOCKS 100 //------------------------------------------------------------------------------ #define JPGD_MAX_HEIGHT 8192 #define JPGD_MAX_WIDTH 8192 //------------------------------------------------------------------------------ /* JPEG specific errors */ #define JPGD_BAD_DHT_COUNTS -200 #define JPGD_BAD_DHT_INDEX -201 #define JPGD_BAD_DHT_MARKER -202 #define JPGD_BAD_DQT_MARKER -203 #define JPGD_BAD_DQT_TABLE -204 #define JPGD_BAD_PRECISION -205 #define JPGD_BAD_HEIGHT -206 #define JPGD_BAD_WIDTH -207 #define JPGD_TOO_MANY_COMPONENTS -208 #define JPGD_BAD_SOF_LENGTH -209 #define JPGD_BAD_VARIABLE_MARKER -210 #define JPGD_BAD_DRI_LENGTH -211 #define JPGD_BAD_SOS_LENGTH -212 #define JPGD_BAD_SOS_COMP_ID -213 #define JPGD_W_EXTRA_BYTES_BEFORE_MARKER -214 #define JPGD_NO_ARITHMITIC_SUPPORT -215 #define JPGD_UNEXPECTED_MARKER -216 #define JPGD_NOT_JPEG -217 #define JPGD_UNSUPPORTED_MARKER -218 #define JPGD_BAD_DQT_LENGTH -219 #define JPGD_TOO_MANY_BLOCKS -221 #define JPGD_UNDEFINED_QUANT_TABLE -222 #define JPGD_UNDEFINED_HUFF_TABLE -223 #define JPGD_NOT_SINGLE_SCAN -224 #define JPGD_UNSUPPORTED_COLORSPACE -225 #define JPGD_UNSUPPORTED_SAMP_FACTORS -226 #define JPGD_DECODE_ERROR -227 #define JPGD_BAD_RESTART_MARKER -228 #define JPGD_ASSERTION_ERROR -229 #define JPGD_BAD_SOS_SPECTRAL -230 #define JPGD_BAD_SOS_SUCCESSIVE -231 #define JPGD_STREAM_READ -232 #define JPGD_NOTENOUGHMEM -233 //------------------------------------------------------------------------------ #define JPGD_GRAYSCALE 0 #define JPGD_YH1V1 1 #define JPGD_YH2V1 2 #define JPGD_YH1V2 3 #define JPGD_YH2V2 4 //------------------------------------------------------------------------------ const int JPGD_FAILED = -1; const int JPGD_DONE = 1; const int JPGD_OKAY = 0; //------------------------------------------------------------------------------ typedef enum { M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100 } JPEG_MARKER; //------------------------------------------------------------------------------ #define RST0 0xD0 //------------------------------------------------------------------------------ typedef struct huff_tables_tag { unsigned int look_up[256]; unsigned char code_size[256]; // FIXME: Is 512 tree entries really enough to handle _all_ possible // code sets? I think so but not 100% positive. unsigned int tree[512]; } huff_tables_t, *Phuff_tables_t; //------------------------------------------------------------------------------ typedef struct coeff_buf_tag { unsigned char *Pdata; int block_num_x, block_num_y; int block_len_x, block_len_y; int block_size; } coeff_buf_t, *Pcoeff_buf_t; //------------------------------------------------------------------------------ class jpeg_decoder; typedef void (*Pdecode_block_func)(jpeg_decoder *, int, int, int); //------------------------------------------------------------------------------ class progressive_block_decoder { public: static void decode_block_dc_first( jpeg_decoder *Pd, int component_id, int block_x, int block_y); static void decode_block_dc_refine( jpeg_decoder *Pd, int component_id, int block_x, int block_y); static void decode_block_ac_first( jpeg_decoder *Pd, int component_id, int block_x, int block_y); static void decode_block_ac_refine( jpeg_decoder *Pd, int component_id, int block_x, int block_y); }; //------------------------------------------------------------------------------ // Input stream interface. // Derive from this class to fetch input data from sources other than // files. An important requirement is that you *must* set eof_flag to true // when no more data is available to fetch! // The decoder is rather "greedy": it will keep on calling this method until // its internal input buffer is full, or until the EOF flag is set. // It the input stream contains data after the JPEG stream's EOI (end of // image) marker it will probably be pulled into the internal buffer. // Call the get_total_bytes_read() method to determine the true // size of the JPEG stream. class jpeg_decoder_stream { public: jpeg_decoder_stream() { } virtual ~jpeg_decoder_stream() { } // The read() method is called when the internal input buffer is empty. // Pbuf - input buffer // max_bytes_to_read - maximum bytes that can be written to Pbuf // Peof_flag - set this to true if at end of stream (no more bytes remaining) // Return -1 on error, otherwise return the number of bytes actually // written to the buffer (which may be 0). // Notes: This method will be called in a loop until you set *Peof_flag to // true or the internal buffer is full. // The MMX state will be automatically saved/restored before this method is // called, unlike previous versions. virtual int read(unsigned char *Pbuf, int max_bytes_to_read, bool *Peof_flag) = 0; virtual void attach(void) { } virtual void detach(void) { } }; //------------------------------------------------------------------------------ typedef jpeg_decoder_stream *Pjpeg_decoder_stream; //------------------------------------------------------------------------------ // Here's an example FILE stream class. class jpeg_decoder_file_stream : public jpeg_decoder_stream { FILE *Pfile; bool eof_flag, error_flag; public: jpeg_decoder_file_stream() { Pfile = NULL; eof_flag = false; error_flag = false; } void close(void) { if (Pfile) { fclose(Pfile); Pfile = NULL; } eof_flag = false; error_flag = false; } virtual ~jpeg_decoder_file_stream() { close(); } bool open(const char *Pfilename) { close(); eof_flag = false; error_flag = false; Pfile = fopen(Pfilename, "rb"); if (!Pfile) return (true); return (false); } virtual int read(unsigned char *Pbuf, int max_bytes_to_read, bool *Peof_flag) { if (!Pfile) return (-1); if (eof_flag) { *Peof_flag = true; return (0); } if (error_flag) return (-1); int bytes_read = fread(Pbuf, 1, max_bytes_to_read, Pfile); if (bytes_read < max_bytes_to_read) { if (ferror(Pfile)) { error_flag = true; return (-1); } eof_flag = true; *Peof_flag = true; } return (bytes_read); } bool get_error_status(void) { return (error_flag); } bool reset(void) { if (error_flag) return (true); fseek(Pfile, 0, SEEK_SET); eof_flag = false; return (false); } int get_size(void) { if (!Pfile) return (-1); int loc = ftell(Pfile); fseek(Pfile, 0, SEEK_END); int size = ftell(Pfile); fseek(Pfile, loc, SEEK_SET); return (size); } }; //------------------------------------------------------------------------------ typedef jpeg_decoder_file_stream *Pjpeg_decoder_file_stream; //------------------------------------------------------------------------------ #define QUANT_TYPE int16 #define BLOCK_TYPE int16 //------------------------------------------------------------------------------ // Disable no return value warning, for rol() method #pragma warning(push) #pragma warning( disable : 4035 4799 ) //------------------------------------------------------------------------------ class jpeg_decoder { friend class progressive_block_decoder; private: void free_all_blocks(void); void terminate(int status); void *alloc(int n); void word_clear(void *p, unsigned short c, unsigned int n); void prep_in_buffer(void); void read_dht_marker(void); void read_dqt_marker(void); void read_sof_marker(void); void skip_variable_marker(void); void read_dri_marker(void); void read_sos_marker(void); int next_marker(void); int process_markers(void); void locate_soi_marker(void); void locate_sof_marker(void); int locate_sos_marker(void); void init(Pjpeg_decoder_stream Pstream, bool use_mmx); void create_look_ups(void); void fix_in_buffer(void); void transform_row(void); Pcoeff_buf_t coeff_buf_open( int block_num_x, int block_num_y, int block_len_x, int block_len_y); void coeff_buf_read( Pcoeff_buf_t cb, int block_x, int block_y, BLOCK_TYPE *buffer); void coeff_buf_write( Pcoeff_buf_t cb, int block_x, int block_y, BLOCK_TYPE *buffer); BLOCK_TYPE *coeff_buf_getp( Pcoeff_buf_t cb, int block_x, int block_y); void load_next_row(void); void decode_next_row(void); void make_huff_table( int index, Phuff_tables_t hs); void check_quant_tables(void); void check_huff_tables(void); void calc_mcu_block_order(void); int init_scan(void); void init_frame(void); void process_restart(void); void decode_scan( Pdecode_block_func decode_block_func); void init_progressive(void); void init_sequential(void); void decode_start(void); void decode_init(Pjpeg_decoder_stream Pstream, bool use_mmx); void H2V2Convert(void); void H2V1Convert(void); void H1V2Convert(void); void H1V1Convert(void); void GrayConvert(void); void find_eoi(void); //------------------ inline unsigned int jpeg_decoder::rol(unsigned int i, unsigned char j); inline unsigned int jpeg_decoder::get_char(void); inline unsigned int jpeg_decoder::get_char(bool *Ppadding_flag); inline void jpeg_decoder::stuff_char(unsigned char q); inline unsigned char jpeg_decoder::get_octet(void); inline unsigned int jpeg_decoder::get_bits_1(int num_bits); inline unsigned int jpeg_decoder::get_bits_2(int numbits); inline int jpeg_decoder::huff_decode(Phuff_tables_t Ph); inline unsigned char jpeg_decoder::clamp(int i); //------------------ int image_x_size; int image_y_size; Pjpeg_decoder_stream Pstream; int progressive_flag; unsigned char *huff_num[JPGD_MAXHUFFTABLES]; /* pointer to number of Huffman codes per bit size */ unsigned char *huff_val[JPGD_MAXHUFFTABLES]; /* pointer to Huffman codes per bit size */ QUANT_TYPE *quant[JPGD_MAXQUANTTABLES]; /* pointer to quantization tables */ int scan_type; /* Grey, Yh1v1, Yh1v2, Yh2v1, Yh2v2, CMYK111, CMYK4114 */ int comps_in_frame; /* # of components in frame */ int comp_h_samp[JPGD_MAXCOMPONENTS]; /* component's horizontal sampling factor */ int comp_v_samp[JPGD_MAXCOMPONENTS]; /* component's vertical sampling factor */ int comp_quant[JPGD_MAXCOMPONENTS]; /* component's quantization table selector */ int comp_ident[JPGD_MAXCOMPONENTS]; /* component's ID */ int comp_h_blocks[JPGD_MAXCOMPONENTS]; int comp_v_blocks[JPGD_MAXCOMPONENTS]; int comps_in_scan; /* # of components in scan */ int comp_list[JPGD_MAXCOMPSINSCAN]; /* components in this scan */ int comp_dc_tab[JPGD_MAXCOMPONENTS]; /* component's DC Huffman coding table selector */ int comp_ac_tab[JPGD_MAXCOMPONENTS]; /* component's AC Huffman coding table selector */ int spectral_start; /* spectral selection start */ int spectral_end; /* spectral selection end */ int successive_low; /* successive approximation low */ int successive_high; /* successive approximation high */ int max_mcu_x_size; /* MCU's max. X size in pixels */ int max_mcu_y_size; /* MCU's max. Y size in pixels */ int blocks_per_mcu; int max_blocks_per_row; int mcus_per_row, mcus_per_col; int mcu_org[JPGD_MAXBLOCKSPERMCU]; int total_lines_left; /* total # lines left in image */ int mcu_lines_left; /* total # lines left in this MCU */ int real_dest_bytes_per_scan_line; int dest_bytes_per_scan_line; /* rounded up */ int dest_bytes_per_pixel; /* currently, 4 (RGB) or 1 (Y) */ void *blocks[JPGD_MAXBLOCKS]; /* list of all dynamically allocated blocks */ Phuff_tables_t h[JPGD_MAXHUFFTABLES]; Pcoeff_buf_t dc_coeffs[JPGD_MAXCOMPONENTS]; Pcoeff_buf_t ac_coeffs[JPGD_MAXCOMPONENTS]; int eob_run; int block_y_mcu[JPGD_MAXCOMPONENTS]; unsigned char *Pin_buf_ofs; int in_buf_left; int tem_flag; bool eof_flag; unsigned char padd_1[128]; unsigned char in_buf[JPGD_INBUFSIZE + 128]; unsigned char padd_2[128]; int bits_left; union { unsigned int bit_buf; unsigned int bit_buf_64[2]; }; unsigned int saved_mm1[2]; bool use_mmx_getbits; int restart_interval; int restarts_left; int next_restart_num; int max_mcus_per_row; int max_blocks_per_mcu; int max_mcus_per_col; unsigned int *component[JPGD_MAXBLOCKSPERMCU]; /* points into the lastdcvals table */ unsigned int last_dc_val[JPGD_MAXCOMPONENTS]; Phuff_tables_t dc_huff_seg[JPGD_MAXBLOCKSPERMCU]; Phuff_tables_t ac_huff_seg[JPGD_MAXBLOCKSPERMCU]; BLOCK_TYPE *block_seg[JPGD_MAXBLOCKSPERROW]; int block_max_zag_set[JPGD_MAXBLOCKSPERROW]; unsigned char *Psample_buf; //int block_num[JPGD_MAXBLOCKSPERROW]; int crr[256]; int cbb[256]; int padd; long crg[256]; long cbg[256]; unsigned char *scan_line_0; unsigned char *scan_line_1; BLOCK_TYPE temp_block[64]; bool use_mmx; bool use_mmx_idct; bool mmx_active; int error_code; bool ready_flag; jmp_buf jmp_state; int total_bytes_read; public: // If SUPPORT_MMX is not defined, the use_mmx flag is ignored. jpeg_decoder(Pjpeg_decoder_stream Pstream, bool use_mmx); int begin(void); int decode(void * *Pscan_line_ofs, unsigned int *Pscan_line_len); ~jpeg_decoder(); int get_error_code(void) { return (error_code); } int get_width(void) { return (image_x_size); } int get_height(void) { return (image_y_size); } int get_num_components(void) { return (comps_in_frame); } int get_bytes_per_pixel(void) { return (dest_bytes_per_pixel); } int get_bytes_per_scan_line(void) { return (image_x_size * get_bytes_per_pixel()); } int get_total_bytes_read(void) { return (total_bytes_read); } }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // inlines-- moved from .h file for clarity //------------------------------------------------------------------------------ // Logical rotate left operation. inline unsigned int jpeg_decoder::rol(unsigned int i, unsigned char j) { return ((i << j) | (i >> (32 - j))); } //------------------------------------------------------------------------------ // Retrieve one character from the input stream. inline unsigned int jpeg_decoder::get_char(void) { // Any bytes remaining in buffer? if (!in_buf_left) { // Try to get more bytes. prep_in_buffer(); // Still nothing to get? if (!in_buf_left) { // Padd the end of the stream with 0xFF 0xD9 (EOI marker) // FIXME: Is there a better padding pattern to use? int t = tem_flag; tem_flag ^= 1; if (t) return (0xD9); else return (0xFF); } } unsigned int c = *Pin_buf_ofs++; in_buf_left--; return (c); } //------------------------------------------------------------------------------ // Same as previus method, except can indicate if the character is // a "padd" character or not. inline unsigned int jpeg_decoder::get_char(bool *Ppadding_flag) { if (!in_buf_left) { prep_in_buffer(); if (!in_buf_left) { *Ppadding_flag = true; int t = tem_flag; tem_flag ^= 1; if (t) return (0xD9); else return (0xFF); } } *Ppadding_flag = false; unsigned int c = *Pin_buf_ofs++; in_buf_left--; return (c); } //------------------------------------------------------------------------------ // Inserts a previously retrieved character back into the input buffer. inline void jpeg_decoder::stuff_char(unsigned char q) { *(--Pin_buf_ofs) = q; in_buf_left++; } //------------------------------------------------------------------------------ // Retrieves one character from the input stream, but does // not read past markers. Will continue to return 0xFF when a // marker is encountered. // FIXME: Bad name? inline unsigned char jpeg_decoder::get_octet(void) { bool padding_flag; int c = get_char(&padding_flag); if (c == 0xFF) { if (padding_flag) return (0xFF); c = get_char(&padding_flag); if (padding_flag) { stuff_char(0xFF); return (0xFF); } if (c == 0x00) return (0xFF); else { stuff_char(c); stuff_char(0xFF); return (0xFF); } } return (c); } //------------------------------------------------------------------------------ // Retrieves a variable number of bits from the input stream. // Does not recognize markers. inline unsigned int jpeg_decoder::get_bits_1(int num_bits) { unsigned int i; i = (bit_buf >> (16 - num_bits)) & ((1 << num_bits) - 1); if ((bits_left -= num_bits) <= 0) { bit_buf = rol(bit_buf, num_bits += bits_left); unsigned int c1 = get_char(); unsigned int c2 = get_char(); bit_buf = (bit_buf & 0xFFFF) | (((unsigned long)c1) << 24) | (((unsigned long)c2) << 16); bit_buf = rol(bit_buf, -bits_left); bits_left += 16; } else bit_buf = rol(bit_buf, num_bits); return i; } //------------------------------------------------------------------------------ // Retrieves a variable number of bits from the input stream. // Markers will not be read into the input bit buffer. Instead, // an infinite number of all 1's will be returned when a marker // is encountered. // FIXME: Is it better to return all 0's instead, like the older implementation? inline unsigned int jpeg_decoder::get_bits_2(int numbits) { unsigned int i; i = (bit_buf >> (16 - numbits)) & ((1 << numbits) - 1); if ((bits_left -= numbits) <= 0) { bit_buf = rol(bit_buf, numbits += bits_left); unsigned int c1 = get_octet(); unsigned int c2 = get_octet(); bit_buf = (bit_buf & 0xFFFF) | (((unsigned long)c1) << 24) | (((unsigned long)c2) << 16); bit_buf = rol(bit_buf, -bits_left); bits_left += 16; } else bit_buf = rol(bit_buf, numbits); return i; } //------------------------------------------------------------------------------ // Decodes a Huffman encoded symbol. inline int jpeg_decoder::huff_decode(Phuff_tables_t Ph) { int symbol; // Check first 8-bits: do we have a complete symbol? if ((symbol = Ph->look_up[(bit_buf >> 8) & 0xFF]) < 0) { // Decode more bits, use a tree traversal to find symbol. get_bits_2(8); do { symbol = Ph->tree[~symbol + (1 - get_bits_2(1))]; } while (symbol < 0); } else get_bits_2(Ph->code_size[symbol]); return symbol; } //------------------------------------------------------------------------------ // Tables and macro used to fully decode the DPCM differences. // (Note: In x86 asm this can be done without using tables.) const int extend_test[16] = /* entry n is 2**(n-1) */ { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; // used by huff_extend() const int extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16), }; #define HUFF_EXTEND_TBL(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) #define HUFF_EXTEND(x,s) HUFF_EXTEND_TBL(x,s) #define HUFF_EXTEND_P(x,s) HUFF_EXTEND_TBL(x,s) //------------------------------------------------------------------------------ // Clamps a value between 0-255. inline unsigned char jpeg_decoder::clamp(int i) { if (i & 0xFFFFFF00) i = (((~i) >> 31) & 0xFF); return (i); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ #pragma warning(pop) //------------------------------------------------------------------------------ typedef jpeg_decoder *Pjpeg_decoder; //------------------------------------------------------------------------------ // idct.cpp void idct(BLOCK_TYPE *data, unsigned char *Pdst_ptr); //------------------------------------------------------------------------------ // fidctfst.cpp void jpeg_idct_ifast ( BLOCK_TYPE* inptr, short *quantptr, unsigned char * *outptr, int output_col); void jpeg_idct_ifast_deinit(void); bool jpeg_idct_ifast_avail(void); //------------------------------------------------------------------------------ #endif //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // h2v2.cpp // Upsampling/colorspace conversion (H2V2, YCbCr) // Last updated: Nov. 16, 2000 // Copyright (C) 1994-2000 Rich Geldreich // richgel@voicenet.com // // This is a popular case, so it's worth seperating out and optimizing a bit. // If you compile this module with the Intel Compiler, the MMX version will // automatically be compiled in. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ #ifdef __ICL #include "mmintrin.h" #endif //------------------------------------------------------------------------------ #define FIX2(x, b) ((long) ((x) * (1L<<(b)) + 0.5)) //------------------------------------------------------------------------------ // YCbCr H2V2 (2x2:1:1, 6 blocks per MCU) to 24-bit RGB // This case is very popular, so it's important that it's fast. // If this module is compiled with the Intel Compiler the faster // MMX specific version will also be available. // FIXME: Create all-asm version, so Intel Compiler isn't needed. void jpeg_decoder::H2V2Convert(void) { int row = max_mcu_y_size - mcu_lines_left; unsigned char *d0 = scan_line_0; unsigned char *d1 = scan_line_1; unsigned char *y; unsigned char *c; #ifdef __ICL // Included here so ICC includes symbol info for these variables static bool init_flag = false; static __m64 CRR_MUL; static __m64 CRR_SUB; static __m64 CBB_MUL; static __m64 CBB_SUB; static __m64 CRG_MUL; static __m64 CBG_MUL; static __m64 GC_SUB; //__m64 rc, gc, bc; __m64 m0,m1,m2,m3,m4,m5; __m64 cr, cb, yy, y0, y1, r, g, b, r0, r1, g0, g1, b0, b1; __m64 rc0, rc1, gc0, gc1, bc0, bc1; static __m64 zero = 0; #endif if (row < 8) y = Psample_buf + row * 8; else y = Psample_buf + 64*2 + (row & 7) * 8; c = Psample_buf + 64*4 + (row >> 1) * 8; #ifdef __ICL if (use_mmx) { if (!init_flag) { init_flag = true; // FIXME: Hardcode these values CRR_MUL = _m_from_int(FIX2(1.402/4, 16)); CRR_SUB = _m_from_int((int)(127.5*1.402 + .5)); CBB_MUL = _m_from_int(FIX2(1.772/4, 16)); CBB_SUB = _m_from_int((int)(127.5*1.772 + .5)); CRG_MUL = _m_from_int(FIX2(-0.71414/4, 16)); CBG_MUL = _m_from_int(FIX2(-0.34414/4, 16)); GC_SUB = _m_from_int((int)(127.5*(-0.71414) + 127.5*(-0.34414) + .5)); CRR_MUL = _m_punpcklwd(CRR_MUL, CRR_MUL); CRR_MUL = _m_punpckldq(CRR_MUL, CRR_MUL); CRR_SUB = _m_punpcklwd(CRR_SUB, CRR_SUB); CRR_SUB = _m_punpckldq(CRR_SUB, CRR_SUB); CBB_MUL = _m_punpcklwd(CBB_MUL, CBB_MUL); CBB_MUL = _m_punpckldq(CBB_MUL, CBB_MUL); CBB_SUB = _m_punpcklwd(CBB_SUB, CBB_SUB); CBB_SUB = _m_punpckldq(CBB_SUB, CBB_SUB); CRG_MUL = _m_punpcklwd(CRG_MUL, CRG_MUL); CRG_MUL = _m_punpckldq(CRG_MUL, CRG_MUL); CBG_MUL = _m_punpcklwd(CBG_MUL, CBG_MUL); CBG_MUL = _m_punpckldq(CBG_MUL, CBG_MUL); GC_SUB = _m_punpcklwd(GC_SUB, GC_SUB); GC_SUB = _m_punpckldq(GC_SUB, GC_SUB); } // Mind-bending MMX intrinsics follow... for (int i = max_mcus_per_row; i > 0; i--) { for (int l = 0; l < 2; l++) { m2 = _m_from_int(*(int *)(&c[0])); m3 = _m_from_int(*(int *)(&c[64])); cb = m2; cb = _m_punpcklbw(cb, cb); cb = _m_psllw(_m_punpcklbw(cb, zero), 2); cr = m3; cr = _m_punpcklbw(cr, cr); cr = _m_psllw(_m_punpcklbw(cr, zero), 2); rc0 = _m_pmulhw(cr, CRR_MUL); rc0 = _m_psubw(rc0, CRR_SUB); bc0 = _m_pmulhw(cb, CBB_MUL); bc0 = _m_psubw(bc0, CBB_SUB); gc0 = _m_pmulhw(cr, CRG_MUL); m0 = _m_pmulhw(cb, CBG_MUL); gc0 = _m_paddw(gc0, m0); gc0 = _m_psubw(gc0, GC_SUB); //------- cb = _m_psrlqi(m2, 16); cb = _m_punpcklbw(cb, cb); cb = _m_psllw(_m_punpcklbw(cb, zero), 2); cr = _m_psrlqi(m3, 16); cr = _m_punpcklbw(cr, cr); cr = _m_psllw(_m_punpcklbw(cr, zero), 2); rc1 = _m_pmulhw(cr, CRR_MUL); rc1 = _m_psubw(rc1, CRR_SUB); bc1 = _m_pmulhw(cb, CBB_MUL); bc1 = _m_psubw(bc1, CBB_SUB); gc1 = _m_pmulhw(cr, CRG_MUL); m0 = _m_pmulhw(cb, CBG_MUL); gc1 = _m_paddw(gc1, m0); gc1 = _m_psubw(gc1, GC_SUB); //------------ yy = *(__m64 *)y; y0 = _m_punpcklbw(yy, zero); y1 = _m_punpcklbw(_m_psrlq(yy, 32), zero); r0 = _m_paddsw(y0, rc0); r1 = _m_paddsw(y1, rc1); g0 = _m_paddsw(y0, gc0); g1 = _m_paddsw(y1, gc1); b0 = _m_paddsw(y0, bc0); b1 = _m_paddsw(y1, bc1); r = _m_packuswb(r0, r1); g = _m_packuswb(g0, g1); b = _m_packuswb(b0, b1); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpcklwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpcklwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpcklwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)d0 = _m_por(_m_por(m0, m1), m2); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpckhwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpckhwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpckhwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d0 + 8) = _m_por(_m_por(m0, m1), m2); //------------ r = _m_psrlqi(r, 32); g = _m_psrlqi(g, 32); b = _m_psrlqi(b, 32); m0 = _m_punpcklbw(r, zero); m0 = _m_punpcklwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpcklwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpcklwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d0 + 16) = _m_por(_m_por(m0, m1), m2); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpckhwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpckhwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpckhwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d0 + 24) = _m_por(_m_por(m0, m1), m2); //------------ //------------ yy = *(__m64 *)(y + 8); y0 = _m_punpcklbw(yy, zero); y1 = _m_punpcklbw(_m_psrlq(yy, 32), zero); r0 = _m_paddsw(y0, rc0); r1 = _m_paddsw(y1, rc1); g0 = _m_paddsw(y0, gc0); g1 = _m_paddsw(y1, gc1); b0 = _m_paddsw(y0, bc0); b1 = _m_paddsw(y1, bc1); r = _m_packuswb(r0, r1); g = _m_packuswb(g0, g1); b = _m_packuswb(b0, b1); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpcklwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpcklwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpcklwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)d1 = _m_por(_m_por(m0, m1), m2); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpckhwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpckhwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpckhwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d1 + 8) = _m_por(_m_por(m0, m1), m2); //------------ r = _m_psrlqi(r, 32); g = _m_psrlqi(g, 32); b = _m_psrlqi(b, 32); m0 = _m_punpcklbw(r, zero); m0 = _m_punpcklwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpcklwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpcklwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d1 + 16) = _m_por(_m_por(m0, m1), m2); //------------ m0 = _m_punpcklbw(r, zero); m0 = _m_punpckhwd(m0, zero); m1 = _m_punpcklbw(g, zero); m1 = _m_punpckhwd(m1, zero); m1 = _m_psllqi(m1, 8); m2 = _m_punpcklbw(b, zero); m2 = _m_punpckhwd(m2, zero); m2 = _m_psllqi(m2, 16); *(__m64 *)(d1 + 24) = _m_por(_m_por(m0, m1), m2); //------------ d0 += 8*4; d1 += 8*4; c += 4; y += 64; } y += 64*6 - 64*2; c += 64*6 - 8; } _m_empty(); } else { #endif for (int i = max_mcus_per_row; i > 0; i--) { for (int l = 0; l < 2; l++) { for (int j = 0; j < 8; j += 2) { int cb = c[0]; int cr = c[64]; int rc = crr[cr]; int gc = ((crg[cr] + cbg[cb]) >> 16); int bc = cbb[cb]; int yy = y[j]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); yy = y[j+1]; d0[4] = clamp(yy+rc); d0[5] = clamp(yy+gc); d0[6] = clamp(yy+bc); yy = y[j+8]; d1[0] = clamp(yy+rc); d1[1] = clamp(yy+gc); d1[2] = clamp(yy+bc); yy = y[j+8+1]; d1[4] = clamp(yy+rc); d1[5] = clamp(yy+gc); d1[6] = clamp(yy+bc); d0 += 8; d1 += 8; c++; } y += 64; } y += 64*6 - 64*2; c += 64*6 - 8; } #ifdef __ICL } #endif } // // 2D IDCT // Derived from an older version of the IJG's JPEG software. // Downloadable from: www.ijg.org // This module is going to be replaced with a faster (and // uncopyrighted) version. // I am unable to find the original file from which this code was derived. // I have included the copyright notice included with latest IJG version of this // module. // /* * jidctint.c * * Copyright (C) 1991-1998, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains a slow-but-accurate integer implementation of the * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine * must also perform dequantization of the input coefficients. * * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT * on each row (or vice versa, but it's more convenient to emit a row at * a time). Direct algorithms are also available, but they are much more * complex and seem not to be any faster when reduced to code. * * This implementation is based on an algorithm described in * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. * The primary algorithm described there uses 11 multiplies and 29 adds. * We use their alternate method with 12 multiplies and 32 adds. * The advantage of this method is that no data path contains more than one * multiplication; this allows a very simple and accurate implementation in * scaled fixed-point arithmetic, with a minimal number of shifts. */ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ #define CONST_BITS 13 #define PASS1_BITS 2 #define SCALEDONE ((int32) 1) #define CONST_SCALE (SCALEDONE << CONST_BITS) #define FIX(x) ((int32) ((x) * CONST_SCALE + 0.5)) /*----------------------------------------------------------------------------*/ #define FIX_0_298631336 ((int32) 2446) /* FIX(0.298631336) */ #define FIX_0_390180644 ((int32) 3196) /* FIX(0.390180644) */ #define FIX_0_541196100 ((int32) 4433) /* FIX(0.541196100) */ #define FIX_0_765366865 ((int32) 6270) /* FIX(0.765366865) */ #define FIX_0_899976223 ((int32) 7373) /* FIX(0.899976223) */ #define FIX_1_175875602 ((int32) 9633) /* FIX(1.175875602) */ #define FIX_1_501321110 ((int32) 12299) /* FIX(1.501321110) */ #define FIX_1_847759065 ((int32) 15137) /* FIX(1.847759065) */ #define FIX_1_961570560 ((int32) 16069) /* FIX(1.961570560) */ #define FIX_2_053119869 ((int32) 16819) /* FIX(2.053119869) */ #define FIX_2_562915447 ((int32) 20995) /* FIX(2.562915447) */ #define FIX_3_072711026 ((int32) 25172) /* FIX(3.072711026) */ /*----------------------------------------------------------------------------*/ #define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n)) /*----------------------------------------------------------------------------*/ #define MULTIPLY(var,cnst) ((var) * (cnst)) /*----------------------------------------------------------------------------*/ void idct(BLOCK_TYPE *data, unsigned char *Pdst_ptr) { int32 tmp0, tmp1, tmp2, tmp3; int32 tmp10, tmp11, tmp12, tmp13; int32 z1, z2, z3, z4, z5; register BLOCK_TYPE *dataptr; int rowctr; dataptr = data; for (rowctr = 8-1; rowctr >= 0; rowctr--) { if ((dataptr[1] | dataptr[2] | dataptr[3] | dataptr[4] | dataptr[5] | dataptr[6] | dataptr[7]) == 0) { int16 dcval = (int16) (dataptr[0] << PASS1_BITS); dataptr[0] = dcval; dataptr[1] = dcval; dataptr[2] = dcval; dataptr[3] = dcval; dataptr[4] = dcval; dataptr[5] = dcval; dataptr[6] = dcval; dataptr[7] = dcval; dataptr += 8; /* advance pointer to next row */ continue; } z2 = (int32) dataptr[2]; z3 = (int32) dataptr[6]; z1 = MULTIPLY(z2 + z3, FIX_0_541196100); tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); tmp0 = ((int32) dataptr[0] + (int32) dataptr[4]) << CONST_BITS; tmp1 = ((int32) dataptr[0] - (int32) dataptr[4]) << CONST_BITS; tmp10 = tmp0 + tmp3; tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; tmp0 = (int32) dataptr[7]; tmp1 = (int32) dataptr[5]; tmp2 = (int32) dataptr[3]; tmp3 = (int32) dataptr[1]; z1 = tmp0 + tmp3; z2 = tmp1 + tmp2; z3 = tmp0 + tmp2; z4 = tmp1 + tmp3; z5 = MULTIPLY(z3 + z4, FIX_1_175875602); tmp0 = MULTIPLY(tmp0, FIX_0_298631336); tmp1 = MULTIPLY(tmp1, FIX_2_053119869); tmp2 = MULTIPLY(tmp2, FIX_3_072711026); tmp3 = MULTIPLY(tmp3, FIX_1_501321110); z1 = MULTIPLY(z1, - FIX_0_899976223); z2 = MULTIPLY(z2, - FIX_2_562915447); z3 = MULTIPLY(z3, - FIX_1_961570560); z4 = MULTIPLY(z4, - FIX_0_390180644); z3 += z5; z4 += z5; tmp0 += z1 + z3; tmp1 += z2 + z4; tmp2 += z2 + z3; tmp3 += z1 + z4; dataptr[0] = (int16) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); dataptr[7] = (int16) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); dataptr[1] = (int16) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); dataptr[6] = (int16) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); dataptr[2] = (int16) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); dataptr[5] = (int16) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); dataptr[3] = (int16) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); dataptr[4] = (int16) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); dataptr += 8; } dataptr = data; for (rowctr = 8-1; rowctr >= 0; rowctr--) { int16 i; if ((dataptr[8*1] | dataptr[8*2] | dataptr[8*3] | dataptr[8*4] | dataptr[8*5] | dataptr[8*6] | dataptr[8*7]) == 0) { int16 dcval = (int16) DESCALE((int32) dataptr[0], PASS1_BITS+3); if ((dcval += 128) < 0) dcval = 0; else if (dcval > 255) dcval = 255; Pdst_ptr[8*0] = (unsigned char)dcval; Pdst_ptr[8*1] = (unsigned char)dcval; Pdst_ptr[8*2] = (unsigned char)dcval; Pdst_ptr[8*3] = (unsigned char)dcval; Pdst_ptr[8*4] = (unsigned char)dcval; Pdst_ptr[8*5] = (unsigned char)dcval; Pdst_ptr[8*6] = (unsigned char)dcval; Pdst_ptr[8*7] = (unsigned char)dcval; dataptr++; Pdst_ptr++; continue; } z2 = (int32) dataptr[8*2]; z3 = (int32) dataptr[8*6]; z1 = MULTIPLY(z2 + z3, FIX_0_541196100); tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); tmp0 = ((int32) dataptr[8*0] + (int32) dataptr[8*4]) << CONST_BITS; tmp1 = ((int32) dataptr[8*0] - (int32) dataptr[8*4]) << CONST_BITS; tmp10 = tmp0 + tmp3; tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; tmp0 = (int32) dataptr[8*7]; tmp1 = (int32) dataptr[8*5]; tmp2 = (int32) dataptr[8*3]; tmp3 = (int32) dataptr[8*1]; z1 = tmp0 + tmp3; z2 = tmp1 + tmp2; z3 = tmp0 + tmp2; z4 = tmp1 + tmp3; z5 = MULTIPLY(z3 + z4, FIX_1_175875602); tmp0 = MULTIPLY(tmp0, FIX_0_298631336); tmp1 = MULTIPLY(tmp1, FIX_2_053119869); tmp2 = MULTIPLY(tmp2, FIX_3_072711026); tmp3 = MULTIPLY(tmp3, FIX_1_501321110); z1 = MULTIPLY(z1, - FIX_0_899976223); z2 = MULTIPLY(z2, - FIX_2_562915447); z3 = MULTIPLY(z3, - FIX_1_961570560); z4 = MULTIPLY(z4, - FIX_0_390180644); z3 += z5; z4 += z5; tmp0 += z1 + z3; tmp1 += z2 + z4; tmp2 += z2 + z3; tmp3 += z1 + z4; #define xclamp(i) if (i & 0xFF00) i = (((~i) >> 15) & 0xFF); i = (int16) DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*0] = (unsigned char)i; i = (int16) DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*7] = (unsigned char)i; i = (int16) DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*1] = (unsigned char)i; i = (int16) DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*6] = (unsigned char)i; i = (int16) DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*2] = (unsigned char)i; i = (int16) DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*5] = (unsigned char)i; i = (int16) DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*3] = (unsigned char)i; i = (int16) DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+3) + 128; xclamp(i) Pdst_ptr[8*4] = (unsigned char)i; dataptr++; Pdst_ptr++; } } //------------------------------------------------------------------------------ // jpegdecoder.cpp // Small JPEG Decoder Library v0.93b // Last updated: Dec. 28, 2001 // Copyright (C) 1994-2000 Rich Geldreich // richgel@voicenet.com // // Dec. 19, 2001 - fixed dumb bug in the decode_next_row functions that // could cause bogus non-zero coefficients from leaking through in rare cases. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Coefficients are stored in this sequence in the data stream. static int ZAG[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, }; //------------------------------------------------------------------------------ const int AAN_SCALE_BITS = 14; const int IFAST_SCALE_BITS = 2; /* fractional bits in scale factors */ //------------------------------------------------------------------------------ static int16 aan_scales[64] = { 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 }; //------------------------------------------------------------------------------ // Unconditionally frees all allocated blocks. void jpeg_decoder::free_all_blocks(void) { if (Pstream) { Pstream->detach(); Pstream = NULL; } for (int i = 0; i < JPGD_MAXBLOCKS; i++) { free(blocks[i]); blocks[i] = NULL; } } //------------------------------------------------------------------------------ // This method handles all errors. // It could easily be changed to use C++ exceptions. void jpeg_decoder::terminate(int status) { error_code = status; free_all_blocks(); longjmp(jmp_state, status); } //------------------------------------------------------------------------------ // Allocate a block of memory-- store block's address in list for // later deallocation by free_all_blocks(). void *jpeg_decoder::alloc(int n) { // Find a free slot. The number of allocated slots will // always be very low, so a linear search is good enough. int i; for (i = 0; i < JPGD_MAXBLOCKS; i++) { if (blocks[i] == NULL) break; } if (i == JPGD_MAXBLOCKS) terminate(JPGD_TOO_MANY_BLOCKS); void *q = malloc(n + 8); if (q == NULL) terminate(JPGD_NOTENOUGHMEM); memset(q, 0, n + 8); blocks[i] = q; // Round to qword boundry, to avoid misaligned accesses with MMX code return ((void *)(((unsigned int)q + 7) & ~7)); } //------------------------------------------------------------------------------ // Clear buffer to word values. void jpeg_decoder::word_clear(void *p, unsigned short c, unsigned int n) { unsigned short *ps = (unsigned short *)p; while (n) { *ps++ = c; n--; } } //------------------------------------------------------------------------------ // Refill the input buffer. // This method will sit in a loop until (A) the buffer is full or (B) // the stream's read() method reports and end of file condition. void jpeg_decoder::prep_in_buffer(void) { in_buf_left = 0; Pin_buf_ofs = in_buf; if (eof_flag) return; do { int bytes_read = Pstream->read(in_buf + in_buf_left, JPGD_INBUFSIZE - in_buf_left, &eof_flag); if (bytes_read == -1) terminate(JPGD_STREAM_READ); in_buf_left += bytes_read; } while ((in_buf_left < JPGD_INBUFSIZE) && (!eof_flag)); total_bytes_read += in_buf_left; word_clear(Pin_buf_ofs + in_buf_left, 0xD9FF, 64); } //------------------------------------------------------------------------------ // Read a Huffman code table. void jpeg_decoder::read_dht_marker(void) { int i, index, count; unsigned int left; unsigned char huff_num[17]; unsigned char huff_val[256]; left = get_bits_1(16); if (left < 2) terminate(JPGD_BAD_DHT_MARKER); left -= 2; while (left) { index = get_bits_1(8); huff_num[0] = 0; count = 0; for (i = 1; i <= 16; i++) { huff_num[i] = get_bits_1(8); count += huff_num[i]; } if (count > 255) terminate(JPGD_BAD_DHT_COUNTS); for (i = 0; i < count; i++) huff_val[i] = get_bits_1(8); i = 1 + 16 + count; if (left < (unsigned int)i) terminate(JPGD_BAD_DHT_MARKER); left -= i; if ((index & 0x10) > 0x10) terminate(JPGD_BAD_DHT_INDEX); index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAXHUFFTABLES >> 1); if (index >= JPGD_MAXHUFFTABLES) terminate(JPGD_BAD_DHT_INDEX); if (!this->huff_num[index]) this->huff_num[index] = (unsigned char *)alloc(17); if (!this->huff_val[index]) this->huff_val[index] = (unsigned char *)alloc(256); memcpy(this->huff_num[index], huff_num, 17); memcpy(this->huff_val[index], huff_val, 256); } } //------------------------------------------------------------------------------ // Read a quantization table. void jpeg_decoder::read_dqt_marker(void) { int n, i, prec; unsigned int left; unsigned int temp; left = get_bits_1(16); if (left < 2) terminate(JPGD_BAD_DQT_MARKER); left -= 2; while (left) { n = get_bits_1(8); prec = n >> 4; n &= 0x0F; if (n >= JPGD_MAXQUANTTABLES) terminate(JPGD_BAD_DQT_TABLE); if (!quant[n]) quant[n] = (QUANT_TYPE *)alloc(64 * sizeof(QUANT_TYPE)); // read quantization entries, in zag order for (i = 0; i < 64; i++) { temp = get_bits_1(8); if (prec) temp = (temp << 8) + get_bits_1(8); if (use_mmx_idct) quant[n][ZAG[i]] = (temp * aan_scales[ZAG[i]] + (1 << (AAN_SCALE_BITS - IFAST_SCALE_BITS - 1))) >> (AAN_SCALE_BITS - IFAST_SCALE_BITS); else quant[n][i] = temp; } i = 64 + 1; if (prec) i += 64; if (left < (unsigned int)i) terminate(JPGD_BAD_DQT_LENGTH); left -= i; } } //------------------------------------------------------------------------------ // Read the start of frame (SOF) marker. void jpeg_decoder::read_sof_marker(void) { int i; unsigned int left; left = get_bits_1(16); if (get_bits_1(8) != 8) /* precision: sorry, only 8-bit precision is supported right now */ terminate(JPGD_BAD_PRECISION); image_y_size = get_bits_1(16); if ((image_y_size < 1) || (image_y_size > JPGD_MAX_HEIGHT)) terminate(JPGD_BAD_HEIGHT); image_x_size = get_bits_1(16); if ((image_x_size < 1) || (image_x_size > JPGD_MAX_WIDTH)) terminate(JPGD_BAD_WIDTH); comps_in_frame = get_bits_1(8); if (comps_in_frame > JPGD_MAXCOMPONENTS) terminate(JPGD_TOO_MANY_COMPONENTS); if (left != (unsigned int)(comps_in_frame * 3 + 8)) terminate(JPGD_BAD_SOF_LENGTH); for (i = 0; i < comps_in_frame; i++) { comp_ident[i] = get_bits_1(8); comp_h_samp[i] = get_bits_1(4); comp_v_samp[i] = get_bits_1(4); comp_quant[i] = get_bits_1(8); } } //------------------------------------------------------------------------------ // Used to skip unrecognized markers. void jpeg_decoder::skip_variable_marker(void) { unsigned int left; left = get_bits_1(16); if (left < 2) terminate(JPGD_BAD_VARIABLE_MARKER); left -= 2; while (left) { get_bits_1(8); left--; } } //------------------------------------------------------------------------------ // Read a define restart interval (DRI) marker. void jpeg_decoder::read_dri_marker(void) { if (get_bits_1(16) != 4) terminate(JPGD_BAD_DRI_LENGTH); restart_interval = get_bits_1(16); } //------------------------------------------------------------------------------ // Read a start of scan (SOS) marker. void jpeg_decoder::read_sos_marker(void) { unsigned int left; int i, ci, n, c, cc; left = get_bits_1(16); n = get_bits_1(8); comps_in_scan = n; left -= 3; if ( (left != (unsigned int)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAXCOMPSINSCAN) ) terminate(JPGD_BAD_SOS_LENGTH); for (i = 0; i < n; i++) { cc = get_bits_1(8); c = get_bits_1(8); left -= 2; for (ci = 0; ci < comps_in_frame; ci++) if (cc == comp_ident[ci]) break; if (ci >= comps_in_frame) terminate(JPGD_BAD_SOS_COMP_ID); comp_list[i] = ci; comp_dc_tab[ci] = (c >> 4) & 15; comp_ac_tab[ci] = (c & 15) + (JPGD_MAXHUFFTABLES >> 1); } spectral_start = get_bits_1(8); spectral_end = get_bits_1(8); successive_high = get_bits_1(4); successive_low = get_bits_1(4); if (!progressive_flag) { spectral_start = 0; spectral_end = 63; } left -= 3; while (left) /* read past whatever is left */ { get_bits_1(8); left--; } } //------------------------------------------------------------------------------ // Finds the next marker. int jpeg_decoder::next_marker(void) { unsigned int c, bytes; bytes = 0; do { do { bytes++; c = get_bits_1(8); } while (c != 0xFF); do { c = get_bits_1(8); } while (c == 0xFF); } while (c == 0); // If bytes > 0 here, there where extra bytes before the marker (not good). return c; } //------------------------------------------------------------------------------ // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is // encountered. int jpeg_decoder::process_markers(void) { int c; for ( ; ; ) { c = next_marker(); switch (c) { case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: // case M_JPG: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: case M_SOI: case M_EOI: case M_SOS: { return c; } case M_DHT: { read_dht_marker(); break; } // Sorry, no arithmitic support at this time. Dumb patents! case M_DAC: { terminate(JPGD_NO_ARITHMITIC_SUPPORT); break; } case M_DQT: { read_dqt_marker(); break; } case M_DRI: { read_dri_marker(); break; } //case M_APP0: /* no need to read the JFIF marker */ case M_JPG: case M_RST0: /* no parameters */ case M_RST1: case M_RST2: case M_RST3: case M_RST4: case M_RST5: case M_RST6: case M_RST7: case M_TEM: { terminate(JPGD_UNEXPECTED_MARKER); break; } default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ { skip_variable_marker(); break; } } } } //------------------------------------------------------------------------------ // Finds the start of image (SOI) marker. // This code is rather defensive: it only checks the first 512 bytes to avoid // false positives. void jpeg_decoder::locate_soi_marker(void) { unsigned int lastchar, thischar; unsigned long bytesleft; lastchar = get_bits_1(8); thischar = get_bits_1(8); /* ok if it's a normal JPEG file without a special header */ if ((lastchar == 0xFF) && (thischar == M_SOI)) return; bytesleft = 512; for ( ; ; ) { if (--bytesleft == 0) terminate(JPGD_NOT_JPEG); lastchar = thischar; thischar = get_bits_1(8); if ((lastchar == 0xFF) && (thischar == M_SOI)) break; } /* Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so it probably isn't a JPEG */ thischar = (bit_buf >> 8) & 0xFF; if (thischar != 0xFF) terminate(JPGD_NOT_JPEG); } //------------------------------------------------------------------------------ // Find a start of frame (SOF) marker. void jpeg_decoder::locate_sof_marker(void) { int c; locate_soi_marker(); c = process_markers(); switch (c) { case M_SOF2: progressive_flag = TRUE; case M_SOF0: /* baseline DCT */ case M_SOF1: /* extended sequential DCT */ { read_sof_marker(); break; } case M_SOF9: /* Arithmitic coding */ { terminate(JPGD_NO_ARITHMITIC_SUPPORT); break; } default: { terminate(JPGD_UNSUPPORTED_MARKER); break; } } } //------------------------------------------------------------------------------ // Find a start of scan (SOS) marker. int jpeg_decoder::locate_sos_marker(void) { int c; c = process_markers(); if (c == M_EOI) return FALSE; else if (c != M_SOS) terminate(JPGD_UNEXPECTED_MARKER); read_sos_marker(); return TRUE; } //------------------------------------------------------------------------------ // Reset everything to default/uninitialized state. void jpeg_decoder::init(Pjpeg_decoder_stream Pstream, bool use_mmx) { error_code = 0; ready_flag = false; image_x_size = image_y_size = 0; this->Pstream = Pstream; this->use_mmx = false; use_mmx_idct = false; progressive_flag = FALSE; memset(huff_num, 0, sizeof(huff_num)); memset(huff_val, 0, sizeof(huff_val)); memset(quant, 0, sizeof(quant)); scan_type = 0; comps_in_frame = 0; memset(comp_h_samp, 0, sizeof(comp_h_samp)); memset(comp_v_samp, 0, sizeof(comp_v_samp)); memset(comp_quant, 0, sizeof(comp_quant)); memset(comp_ident, 0, sizeof(comp_ident)); memset(comp_h_blocks, 0, sizeof(comp_h_blocks)); memset(comp_v_blocks, 0, sizeof(comp_v_blocks)); comps_in_scan = 0; memset(comp_list, 0, sizeof(comp_list)); memset(comp_dc_tab, 0, sizeof(comp_dc_tab)); memset(comp_ac_tab, 0, sizeof(comp_ac_tab)); spectral_start = 0; spectral_end = 0; successive_low = 0; successive_high = 0; max_mcu_x_size = 0; max_mcu_y_size = 0; blocks_per_mcu = 0; max_blocks_per_row = 0; mcus_per_row = 0; mcus_per_col = 0; memset(mcu_org, 0, sizeof(mcu_org)); total_lines_left = 0; mcu_lines_left = 0; real_dest_bytes_per_scan_line = 0; dest_bytes_per_scan_line = 0; dest_bytes_per_pixel = 0; memset(blocks, 0, sizeof(blocks)); memset(h, 0, sizeof(h)); memset(dc_coeffs, 0, sizeof(dc_coeffs)); memset(ac_coeffs, 0, sizeof(ac_coeffs)); memset(block_y_mcu, 0, sizeof(block_y_mcu)); eob_run = 0; memset(block_y_mcu, 0, sizeof(block_y_mcu)); Pin_buf_ofs = in_buf; in_buf_left = 0; eof_flag = false; tem_flag = 0; memset(padd_1, 0, sizeof(padd_1)); memset(in_buf, 0, sizeof(in_buf)); memset(padd_2, 0, sizeof(padd_2)); restart_interval = 0; restarts_left = 0; next_restart_num = 0; max_mcus_per_row = 0; max_blocks_per_mcu = 0; max_mcus_per_col = 0; memset(component, 0, sizeof(component)); memset(last_dc_val, 0, sizeof(last_dc_val)); memset(dc_huff_seg, 0, sizeof(dc_huff_seg)); memset(ac_huff_seg, 0, sizeof(ac_huff_seg)); memset(block_seg, 0, sizeof(block_seg)); Psample_buf = NULL; total_bytes_read = 0; // Tell the stream we're going to use it. Pstream->attach(); use_mmx_getbits = false; mmx_active = false; // Ready the input buffer. prep_in_buffer(); // Prime the bit buffer. bits_left = 16; bit_buf_64[0] = 0; bit_buf_64[1] = 0; get_bits_1(16); get_bits_1(16); for (int i = 0; i < JPGD_MAXBLOCKSPERROW; i++) block_max_zag_set[i] = 64; } //------------------------------------------------------------------------------ #define SCALEBITS 16 #define ONE_HALF ((long) 1 << (SCALEBITS-1)) #undef FIX #define FIX(x) ((long) ((x) * (1L<> SCALEBITS; cbb[i] = ( FIX(1.77200/2) * k + ONE_HALF) >> SCALEBITS; crg[i] = (-FIX(0.71414/2)) * k; cbg[i] = (-FIX(0.34414/2)) * k + ONE_HALF; } } //------------------------------------------------------------------------------ // This method throws back into the stream any bytes that where read // into the bit buffer during initial marker scanning. void jpeg_decoder::fix_in_buffer(void) { /* In case any 0xFF's where pulled into the buffer during marker scanning */ if (bits_left == 16) stuff_char( (unsigned char)((bit_buf >> 16) & 0xFF)); if (bits_left >= 8) stuff_char( (unsigned char)((bit_buf >> 24) & 0xFF)); stuff_char( (unsigned char)(bit_buf & 0xFF) ); stuff_char( (unsigned char)((bit_buf >> 8) & 0xFF) ); { bits_left = 16; //bit_buf = 0; get_bits_2(16); get_bits_2(16); } } //------------------------------------------------------------------------------ // Performs a 2D IDCT over the entire row's coefficient buffer. void jpeg_decoder::transform_row(void) { { BLOCK_TYPE *Psrc_ptr = block_seg[0]; unsigned char *Pdst_ptr = Psample_buf; for (int i = max_blocks_per_row; i > 0; i--) { // Copy the block to a temp. buffer to prevent the IDCT // from modifying the entire block. memcpy(temp_block, Psrc_ptr, 64 * sizeof(BLOCK_TYPE)); idct(temp_block, Pdst_ptr); Psrc_ptr += 64; Pdst_ptr += 64; } } } //------------------------------------------------------------------------------ // The coeff_buf series of methods originally stored the coefficients // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache // was used to make this process more efficient. Now, we can store the entire // thing in RAM. Pcoeff_buf_t jpeg_decoder::coeff_buf_open( int block_num_x, int block_num_y, int block_len_x, int block_len_y) { Pcoeff_buf_t cb = (Pcoeff_buf_t)alloc(sizeof(coeff_buf_t)); cb->block_num_x = block_num_x; cb->block_num_y = block_num_y; cb->block_len_x = block_len_x; cb->block_len_y = block_len_y; cb->block_size = (block_len_x * block_len_y) * sizeof(BLOCK_TYPE); cb->Pdata = (unsigned char *)alloc(cb->block_size * block_num_x * block_num_y); return cb; } //------------------------------------------------------------------------------ void jpeg_decoder::coeff_buf_read( Pcoeff_buf_t cb, int block_x, int block_y, BLOCK_TYPE *buffer) { if (block_x >= cb->block_num_x) terminate(JPGD_ASSERTION_ERROR); if (block_y >= cb->block_num_y) terminate(JPGD_ASSERTION_ERROR); memcpy(buffer, cb->Pdata + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x), cb->block_size); } //------------------------------------------------------------------------------ void jpeg_decoder::coeff_buf_write( Pcoeff_buf_t cb, int block_x, int block_y, BLOCK_TYPE *buffer) { if (block_x >= cb->block_num_x) terminate(JPGD_ASSERTION_ERROR); if (block_y >= cb->block_num_y) terminate(JPGD_ASSERTION_ERROR); memcpy(cb->Pdata + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x), buffer, cb->block_size); } //------------------------------------------------------------------------------ BLOCK_TYPE *jpeg_decoder::coeff_buf_getp( Pcoeff_buf_t cb, int block_x, int block_y) { if (block_x >= cb->block_num_x) terminate(JPGD_ASSERTION_ERROR); if (block_y >= cb->block_num_y) terminate(JPGD_ASSERTION_ERROR); return (BLOCK_TYPE *)(cb->Pdata + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x)); } //------------------------------------------------------------------------------ // Loads and dequantizes the next row of (already decoded) coefficients. // Progressive images only. void jpeg_decoder::load_next_row(void) { int i; BLOCK_TYPE *p; QUANT_TYPE *q; int mcu_row, mcu_block, row_block = 0; int component_num, component_id; int block_x_mcu[JPGD_MAXCOMPONENTS]; memset(block_x_mcu, 0, JPGD_MAXCOMPONENTS * sizeof(int)); for (mcu_row = 0; mcu_row < mcus_per_row; mcu_row++) { int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; for (mcu_block = 0; mcu_block < blocks_per_mcu; mcu_block++) { component_id = mcu_org[mcu_block]; p = block_seg[row_block]; q = quant[comp_quant[component_id]]; BLOCK_TYPE *pAC = coeff_buf_getp(ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, block_y_mcu[component_id] + block_y_mcu_ofs); BLOCK_TYPE *pDC = coeff_buf_getp(dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, block_y_mcu[component_id] + block_y_mcu_ofs); p[0] = pDC[0]; memcpy(&p[1], &pAC[1], 63 * sizeof(BLOCK_TYPE)); if (!use_mmx_idct) { for (i = 63; i > 0; i--) if (p[ZAG[i]]) break; //block_num[row_block++] = i + 1; for ( ; i >= 0; i--) if (p[ZAG[i]]) p[ZAG[i]] *= q[i]; } row_block++; if (comps_in_scan == 1) block_x_mcu[component_id]++; else { if (++block_x_mcu_ofs == comp_h_samp[component_id]) { block_x_mcu_ofs = 0; if (++block_y_mcu_ofs == comp_v_samp[component_id]) { block_y_mcu_ofs = 0; block_x_mcu[component_id] += comp_h_samp[component_id]; } } } } } if (comps_in_scan == 1) block_y_mcu[comp_list[0]]++; else { for (component_num = 0; component_num < comps_in_scan; component_num++) { component_id = comp_list[component_num]; block_y_mcu[component_id] += comp_v_samp[component_id]; } } } //------------------------------------------------------------------------------ // Restart interval processing. void jpeg_decoder::process_restart(void) { int i, c; // Align to a byte boundry // FIXME: Is this really necessary? get_bits_2() never reads in markers! //get_bits_2(bits_left & 7); // Let's scan a little bit to find the marker, but not _too_ far. // 1536 is a "fudge factor" that determines how much to scan. for (i = 1536; i > 0; i--) if (get_char() == 0xFF) break; if (i == 0) terminate(JPGD_BAD_RESTART_MARKER); for ( ; i > 0; i--) if ((c = get_char()) != 0xFF) break; if (i == 0) terminate(JPGD_BAD_RESTART_MARKER); // Is it the expected marker? If not, something bad happened. if (c != (next_restart_num + M_RST0)) terminate(JPGD_BAD_RESTART_MARKER); // Reset each component's DC prediction values. memset(&last_dc_val, 0, comps_in_frame * sizeof(unsigned int)); eob_run = 0; restarts_left = restart_interval; next_restart_num = (next_restart_num + 1) & 7; // Get the bit buffer going again... { bits_left = 16; //bit_buf = 0; get_bits_2(16); get_bits_2(16); } } //------------------------------------------------------------------------------ // Decodes and dequantizes the next row of coefficients. void jpeg_decoder::decode_next_row(void) { int row_block = 0; // Clearing the entire row block buffer can take a lot of time! // Instead of clearing the entire buffer each row, keep track // of the number of nonzero entries written to each block and do // selective clears. //memset(block_seg[0], 0, mcus_per_row * blocks_per_mcu * 64 * sizeof(BLOCK_TYPE)); for (int mcu_row = 0; mcu_row < mcus_per_row; mcu_row++) { if ((restart_interval) && (restarts_left == 0)) process_restart(); for (int mcu_block = 0; mcu_block < blocks_per_mcu; mcu_block++) { int component_id = mcu_org[mcu_block]; BLOCK_TYPE *p = block_seg[row_block]; QUANT_TYPE *q = quant[comp_quant[component_id]]; int r, s; if ((s = huff_decode(h[comp_dc_tab[component_id]])) != 0) { r = get_bits_2(s); s = HUFF_EXTEND(r, s); } last_dc_val[component_id] = (s += last_dc_val[component_id]); if (use_mmx_idct) p[0] = s; else p[0] = s * q[0]; int prev_num_set = block_max_zag_set[row_block]; Phuff_tables_t Ph = h[comp_ac_tab[component_id]]; int k; for (k = 1; k < 64; k++) { s = huff_decode(Ph); r = s >> 4; s &= 15; if (s) { if (r) { if ((k + r) > 63) terminate(JPGD_DECODE_ERROR); if (k < prev_num_set) { int n = min(r, prev_num_set - k); int kt = k; while (n--) p[ZAG[kt++]] = 0; } k += r; } r = get_bits_2(s); s = HUFF_EXTEND(r, s); //assert(k < 64); if (use_mmx_idct) p[ZAG[k]] = s; else p[ZAG[k]] = s * q[k]; } else { if (r == 15) { if ((k + 15) > 63) terminate(JPGD_DECODE_ERROR); if (k < prev_num_set) { int n = min(16, prev_num_set - k); //bugfix Dec. 19, 2001 - was 15! int kt = k; while (n--) p[ZAG[kt++]] = 0; } k += 15; } else { //while (k < 64) // p[ZAG[k++]] = 0; break; } } } if (k < prev_num_set) { int kt = k; while (kt < prev_num_set) p[ZAG[kt++]] = 0; } block_max_zag_set[row_block] = k; //block_num[row_block++] = k; row_block++; } restarts_left--; } } //------------------------------------------------------------------------------ // YCbCr H1V1 (1x1:1:1, 3 blocks per MCU) to 24-bit RGB void jpeg_decoder::H1V1Convert(void) { int row = max_mcu_y_size - mcu_lines_left; unsigned char *d = scan_line_0; unsigned char *s = Psample_buf + row * 8; for (int i = max_mcus_per_row; i > 0; i--) { for (int j = 0; j < 8; j++) { int y = s[j]; int cb = s[64+j]; int cr = s[128+j]; d[0] = clamp(y + crr[cr]); d[1] = clamp(y + ((crg[cr] + cbg[cb]) >> 16)); d[2] = clamp(y + cbb[cb]); d += 4; } s += 64*3; } } //------------------------------------------------------------------------------ // YCbCr H2V1 (2x1:1:1, 4 blocks per MCU) to 24-bit RGB void jpeg_decoder::H2V1Convert(void) { int row = max_mcu_y_size - mcu_lines_left; unsigned char *d0 = scan_line_0; unsigned char *y = Psample_buf + row * 8; unsigned char *c = Psample_buf + 2*64 + row * 8; for (int i = max_mcus_per_row; i > 0; i--) { for (int l = 0; l < 2; l++) { for (int j = 0; j < 4; j++) { int cb = c[0]; int cr = c[64]; int rc = crr[cr]; int gc = ((crg[cr] + cbg[cb]) >> 16); int bc = cbb[cb]; int yy = y[j<<1]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); yy = y[(j<<1)+1]; d0[4] = clamp(yy+rc); d0[5] = clamp(yy+gc); d0[6] = clamp(yy+bc); d0 += 8; c++; } y += 64; } y += 64*4 - 64*2; c += 64*4 - 8; } } //------------------------------------------------------------------------------ // YCbCr H2V1 (1x2:1:1, 4 blocks per MCU) to 24-bit RGB void jpeg_decoder::H1V2Convert(void) { int row = max_mcu_y_size - mcu_lines_left; unsigned char *d0 = scan_line_0; unsigned char *d1 = scan_line_1; unsigned char *y; unsigned char *c; if (row < 8) y = Psample_buf + row * 8; else y = Psample_buf + 64*1 + (row & 7) * 8; c = Psample_buf + 64*2 + (row >> 1) * 8; for (int i = max_mcus_per_row; i > 0; i--) { for (int j = 0; j < 8; j++) { int cb = c[0+j]; int cr = c[64+j]; int rc = crr[cr]; int gc = ((crg[cr] + cbg[cb]) >> 16); int bc = cbb[cb]; int yy = y[j]; d0[0] = clamp(yy+rc); d0[1] = clamp(yy+gc); d0[2] = clamp(yy+bc); yy = y[8+j]; d1[0] = clamp(yy+rc); d1[1] = clamp(yy+gc); d1[2] = clamp(yy+bc); d0 += 4; d1 += 4; } y += 64*4; c += 64*4; } } //------------------------------------------------------------------------------ // Y (1 block per MCU) to 8-bit greyscale void jpeg_decoder::GrayConvert(void) { int row = max_mcu_y_size - mcu_lines_left; unsigned char *d = scan_line_0; unsigned char *s = Psample_buf + row * 8; for (int i = max_mcus_per_row; i > 0; i--) { #if 0 d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3]; d[4] = s[4]; d[5] = s[5]; d[6] = s[6]; d[7] = s[7]; #endif *(unsigned int *)d = *(unsigned int *)s; *(unsigned int *)(&d[4]) = *(unsigned int *)(&s[4]); s += 64; d += 8; } } //------------------------------------------------------------------------------ // Find end of image (EOI) marker, so we can return to the user the // exact size of the input stream. void jpeg_decoder::find_eoi(void) { if (!progressive_flag) { // Attempt to read the EOI marker. //get_bits_2(bits_left & 7); // Prime the bit buffer bits_left = 16; //bit_buf = 0; get_bits_1(16); get_bits_1(16); // The next marker _should_ be EOI process_markers(); } total_bytes_read -= in_buf_left; } //------------------------------------------------------------------------------ // Returns the next scan line. // Returns JPGD_DONE if all scan lines have been returned. // Returns JPGD_OKAY if a scan line has been returned. // Returns JPGD_FAILED if an error occured. int jpeg_decoder::decode( void * *Pscan_line_ofs, unsigned int *Pscan_line_len) { if ((error_code) || (!ready_flag)) return (JPGD_FAILED); if (total_lines_left == 0) return (JPGD_DONE); if (mcu_lines_left == 0) { if (setjmp(jmp_state)) return (JPGD_DECODE_ERROR); if (progressive_flag) load_next_row(); else { decode_next_row(); } // Find the EOI marker if that was the last row. if (total_lines_left <= max_mcu_y_size) find_eoi(); transform_row(); mcu_lines_left = max_mcu_y_size; } switch (scan_type) { case JPGD_YH2V2: { if ((mcu_lines_left & 1) == 0) { H2V2Convert(); *Pscan_line_ofs = scan_line_0; } else *Pscan_line_ofs = scan_line_1; break; } case JPGD_YH2V1: { H2V1Convert(); *Pscan_line_ofs = scan_line_0; break; } case JPGD_YH1V2: { if ((mcu_lines_left & 1) == 0) { H1V2Convert(); *Pscan_line_ofs = scan_line_0; } else *Pscan_line_ofs = scan_line_1; break; } case JPGD_YH1V1: { H1V1Convert(); *Pscan_line_ofs = scan_line_0; break; } case JPGD_GRAYSCALE: { GrayConvert(); *Pscan_line_ofs = scan_line_0; break; } } *Pscan_line_len = real_dest_bytes_per_scan_line; mcu_lines_left--; total_lines_left--; return (JPGD_OKAY); } //------------------------------------------------------------------------------ // Creates the tables needed for efficient Huffman decoding. void jpeg_decoder::make_huff_table( int index, Phuff_tables_t hs) { int p, i, l, si; unsigned char huffsize[257]; unsigned int huffcode[257]; unsigned int code; unsigned int subtree; int code_size; int lastp; int nextfreeentry; int currententry; p = 0; for (l = 1; l <= 16; l++) { for (i = 1; i <= huff_num[index][l]; i++) huffsize[p++] = l; } huffsize[p] = 0; lastp = p; code = 0; si = huffsize[0]; p = 0; while (huffsize[p]) { while (huffsize[p] == si) { huffcode[p++] = code; code++; } code <<= 1; si++; } memset(hs->look_up, 0, sizeof(hs->look_up)); memset(hs->tree, 0, sizeof(hs->tree)); memset(hs->code_size, 0, sizeof(hs->code_size)); nextfreeentry = -1; p = 0; while (p < lastp) { i = huff_val[index][p]; code = huffcode[p]; code_size = huffsize[p]; hs->code_size[i] = code_size; if (code_size <= 8) { code <<= (8 - code_size); for (l = 1 << (8 - code_size); l > 0; l--) { hs->look_up[code] = i; code++; } } else { subtree = (code >> (code_size - 8)) & 0xFF; currententry = hs->look_up[subtree]; if (currententry == 0) { hs->look_up[subtree] = currententry = nextfreeentry; nextfreeentry -= 2; } code <<= (16 - (code_size - 8)); for (l = code_size; l > 9; l--) { if ((code & 0x8000) == 0) currententry--; if (hs->tree[-currententry - 1] == 0) { hs->tree[-currententry - 1] = nextfreeentry; currententry = nextfreeentry; nextfreeentry -= 2; } else currententry = hs->tree[-currententry - 1]; code <<= 1; } if ((code & 0x8000) == 0) currententry--; hs->tree[-currententry - 1] = i; } p++; } } //------------------------------------------------------------------------------ // Verifies the quantization tables needed for this scan are available. void jpeg_decoder::check_quant_tables(void) { int i; for (i = 0; i < comps_in_scan; i++) if (quant[comp_quant[comp_list[i]]] == NULL) terminate(JPGD_UNDEFINED_QUANT_TABLE); } //------------------------------------------------------------------------------ // Verifies that all the Huffman tables needed for this scan are available. void jpeg_decoder::check_huff_tables(void) { int i; for (i = 0; i < comps_in_scan; i++) { if ((spectral_start == 0) && (huff_num[comp_dc_tab[comp_list[i]]] == NULL)) terminate(JPGD_UNDEFINED_HUFF_TABLE); if ((spectral_end > 0) && (huff_num[comp_ac_tab[comp_list[i]]] == NULL)) terminate(JPGD_UNDEFINED_HUFF_TABLE); } for (i = 0; i < JPGD_MAXHUFFTABLES; i++) if (huff_num[i]) { if (!h[i]) h[i] = (Phuff_tables_t)alloc(sizeof(huff_tables_t)); make_huff_table(i, h[i]); } for (i = 0; i < blocks_per_mcu; i++) { dc_huff_seg[i] = h[comp_dc_tab[mcu_org[i]]]; ac_huff_seg[i] = h[comp_ac_tab[mcu_org[i]]]; component[i] = &last_dc_val[mcu_org[i]]; } } //------------------------------------------------------------------------------ // Determines the component order inside each MCU. // Also calcs how many MCU's are on each row, etc. void jpeg_decoder::calc_mcu_block_order(void) { int component_num, component_id; int max_h_samp = 0, max_v_samp = 0; for (component_id = 0; component_id < comps_in_frame; component_id++) { if (comp_h_samp[component_id] > max_h_samp) max_h_samp = comp_h_samp[component_id]; if (comp_v_samp[component_id] > max_v_samp) max_v_samp = comp_v_samp[component_id]; } for (component_id = 0; component_id < comps_in_frame; component_id++) { comp_h_blocks[component_id] = ((((image_x_size * comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8; comp_v_blocks[component_id] = ((((image_y_size * comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8; } if (comps_in_scan == 1) { mcus_per_row = comp_h_blocks[comp_list[0]]; mcus_per_col = comp_v_blocks[comp_list[0]]; } else { mcus_per_row = (((image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp; mcus_per_col = (((image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp; } if (comps_in_scan == 1) { mcu_org[0] = comp_list[0]; blocks_per_mcu = 1; } else { blocks_per_mcu = 0; for (component_num = 0; component_num < comps_in_scan; component_num++) { int num_blocks; component_id = comp_list[component_num]; num_blocks = comp_h_samp[component_id] * comp_v_samp[component_id]; while (num_blocks--) mcu_org[blocks_per_mcu++] = component_id; } } } //------------------------------------------------------------------------------ // Starts a new scan. int jpeg_decoder::init_scan(void) { if (!locate_sos_marker()) return FALSE; calc_mcu_block_order(); check_huff_tables(); check_quant_tables(); memset(last_dc_val, 0, comps_in_frame * sizeof(unsigned int)); eob_run = 0; if (restart_interval) { restarts_left = restart_interval; next_restart_num = 0; } if ((!progressive_flag) && (use_mmx)) use_mmx_getbits = true; fix_in_buffer(); return TRUE; } //------------------------------------------------------------------------------ // Starts a frame. Determines if the number of components or sampling factors // are supported. void jpeg_decoder::init_frame(void) { int i; unsigned char *q; if (comps_in_frame == 1) { scan_type = JPGD_GRAYSCALE; max_blocks_per_mcu = 1; max_mcu_x_size = 8; max_mcu_y_size = 8; } else if (comps_in_frame == 3) { if ( ((comp_h_samp[1] != 1) || (comp_v_samp[1] != 1)) || ((comp_h_samp[2] != 1) || (comp_v_samp[2] != 1)) ) terminate(JPGD_UNSUPPORTED_SAMP_FACTORS); if ((comp_h_samp[0] == 1) && (comp_v_samp[0] == 1)) { scan_type = JPGD_YH1V1; max_blocks_per_mcu = 3; max_mcu_x_size = 8; max_mcu_y_size = 8; } else if ((comp_h_samp[0] == 2) && (comp_v_samp[0] == 1)) { scan_type = JPGD_YH2V1; max_blocks_per_mcu = 4; max_mcu_x_size = 16; max_mcu_y_size = 8; } else if ((comp_h_samp[0] == 1) && (comp_v_samp[0] == 2)) { scan_type = JPGD_YH1V2; max_blocks_per_mcu = 4; max_mcu_x_size = 8; max_mcu_y_size = 16; } else if ((comp_h_samp[0] == 2) && (comp_v_samp[0] == 2)) { scan_type = JPGD_YH2V2; max_blocks_per_mcu = 6; max_mcu_x_size = 16; max_mcu_y_size = 16; } else terminate(JPGD_UNSUPPORTED_SAMP_FACTORS); } else terminate(JPGD_UNSUPPORTED_COLORSPACE); max_mcus_per_row = (image_x_size + (max_mcu_x_size - 1)) / max_mcu_x_size; max_mcus_per_col = (image_y_size + (max_mcu_y_size - 1)) / max_mcu_y_size; /* these values are for the *destination* pixels: after conversion */ if (scan_type == JPGD_GRAYSCALE) dest_bytes_per_pixel = 1; else dest_bytes_per_pixel = 4; dest_bytes_per_scan_line = ((image_x_size + 15) & 0xFFF0) * dest_bytes_per_pixel; real_dest_bytes_per_scan_line = (image_x_size * dest_bytes_per_pixel); // Initialize two scan line buffers. // FIXME: Only the V2 sampling factors need two buffers. scan_line_0 = (unsigned char *)alloc(dest_bytes_per_scan_line + 8); memset(scan_line_0, 0, dest_bytes_per_scan_line); scan_line_1 = (unsigned char *)alloc(dest_bytes_per_scan_line + 8); memset(scan_line_1, 0, dest_bytes_per_scan_line); max_blocks_per_row = max_mcus_per_row * max_blocks_per_mcu; // Should never happen if (max_blocks_per_row > JPGD_MAXBLOCKSPERROW) terminate(JPGD_ASSERTION_ERROR); // Allocate the coefficient buffer, enough for one row's worth of MCU's q = (unsigned char *)alloc(max_blocks_per_row * 64 * sizeof(BLOCK_TYPE) + 8); // Align to 8-byte boundry, for MMX code q = (unsigned char *)(((unsigned int)q + 7) & ~7); // The block_seg[] array's name dates back to the // 16-bit assembler implementation. "seg" stood for "segment". for (i = 0; i < max_blocks_per_row; i++) block_seg[i] = (BLOCK_TYPE *)(q + i * 64 * sizeof(BLOCK_TYPE)); for (i = 0; i < max_blocks_per_row; i++) block_max_zag_set[i] = 64; Psample_buf = (unsigned char *)(((unsigned int)alloc(max_blocks_per_row * 64 + 8) + 7) & ~7); total_lines_left = image_y_size; mcu_lines_left = 0; create_look_ups(); } //------------------------------------------------------------------------------ // The following methods decode the various types of blocks encountered // in progressively encoded images. void progressive_block_decoder::decode_block_dc_first( jpeg_decoder *Pd, int component_id, int block_x, int block_y) { int s, r; BLOCK_TYPE *p = Pd->coeff_buf_getp(Pd->dc_coeffs[component_id], block_x, block_y); if ((s = Pd->huff_decode(Pd->h[Pd->comp_dc_tab[component_id]])) != 0) { r = Pd->get_bits_2(s); s = HUFF_EXTEND_P(r, s); } Pd->last_dc_val[component_id] = (s += Pd->last_dc_val[component_id]); p[0] = s << Pd->successive_low; } //------------------------------------------------------------------------------ void progressive_block_decoder::decode_block_dc_refine( jpeg_decoder *Pd, int component_id, int block_x, int block_y) { if (Pd->get_bits_2(1)) { BLOCK_TYPE *p = Pd->coeff_buf_getp(Pd->dc_coeffs[component_id], block_x, block_y); p[0] |= (1 << Pd->successive_low); } } //------------------------------------------------------------------------------ void progressive_block_decoder::decode_block_ac_first( jpeg_decoder *Pd, int component_id, int block_x, int block_y) { int k, s, r; if (Pd->eob_run) { Pd->eob_run--; return; } BLOCK_TYPE *p = Pd->coeff_buf_getp(Pd->ac_coeffs[component_id], block_x, block_y); for (k = Pd->spectral_start; k <= Pd->spectral_end; k++) { s = Pd->huff_decode(Pd->h[Pd->comp_ac_tab[component_id]]); r = s >> 4; s &= 15; if (s) { if ((k += r) > 63) Pd->terminate(JPGD_DECODE_ERROR); r = Pd->get_bits_2(s); s = HUFF_EXTEND_P(r, s); p[ZAG[k]] = s << Pd->successive_low; } else { if (r == 15) { if ((k += 15) > 63) Pd->terminate(JPGD_DECODE_ERROR); } else { Pd->eob_run = 1 << r; if (r) Pd->eob_run += Pd->get_bits_2(r); Pd->eob_run--; break; } } } } //------------------------------------------------------------------------------ void progressive_block_decoder::decode_block_ac_refine( jpeg_decoder *Pd, int component_id, int block_x, int block_y) { int s, k, r; int p1 = 1 << Pd->successive_low; int m1 = (-1) << Pd->successive_low; BLOCK_TYPE *p = Pd->coeff_buf_getp(Pd->ac_coeffs[component_id], block_x, block_y); k = Pd->spectral_start; if (Pd->eob_run == 0) { for ( ; k <= Pd->spectral_end; k++) { s = Pd->huff_decode(Pd->h[Pd->comp_ac_tab[component_id]]); r = s >> 4; s &= 15; if (s) { if (s != 1) Pd->terminate(JPGD_DECODE_ERROR); if (Pd->get_bits_2(1)) s = p1; else s = m1; } else { if (r != 15) { Pd->eob_run = 1 << r; if (r) Pd->eob_run += Pd->get_bits_2(r); break; } } do { BLOCK_TYPE *this_coef = p + ZAG[k]; if (*this_coef != 0) { if (Pd->get_bits_2(1)) { if ((*this_coef & p1) == 0) { if (*this_coef >= 0) *this_coef += p1; else *this_coef += m1; } } } else { if (--r < 0) break; } k++; } while (k <= Pd->spectral_end); if ((s) && (k < 64)) { p[ZAG[k]] = s; } } } if (Pd->eob_run > 0) { for ( ; k <= Pd->spectral_end; k++) { BLOCK_TYPE *this_coef = p + ZAG[k]; if (*this_coef != 0) { if (Pd->get_bits_2(1)) { if ((*this_coef & p1) == 0) { if (*this_coef >= 0) *this_coef += p1; else *this_coef += m1; } } } } Pd->eob_run--; } } //------------------------------------------------------------------------------ // Decode a scan in a progressively encoded image. void jpeg_decoder::decode_scan( Pdecode_block_func decode_block_func) { int mcu_row, mcu_col, mcu_block; int block_x_mcu[JPGD_MAXCOMPONENTS], block_y_mcu[JPGD_MAXCOMPONENTS]; memset(block_y_mcu, 0, sizeof(block_y_mcu)); for (mcu_col = 0; mcu_col < mcus_per_col; mcu_col++) { int component_num, component_id; memset(block_x_mcu, 0, sizeof(block_x_mcu)); for (mcu_row = 0; mcu_row < mcus_per_row; mcu_row++) { int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; if ((restart_interval) && (restarts_left == 0)) process_restart(); for (mcu_block = 0; mcu_block < blocks_per_mcu; mcu_block++) { component_id = mcu_org[mcu_block]; decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, block_y_mcu[component_id] + block_y_mcu_ofs); if (comps_in_scan == 1) block_x_mcu[component_id]++; else { if (++block_x_mcu_ofs == comp_h_samp[component_id]) { block_x_mcu_ofs = 0; if (++block_y_mcu_ofs == comp_v_samp[component_id]) { block_y_mcu_ofs = 0; block_x_mcu[component_id] += comp_h_samp[component_id]; } } } } restarts_left--; } if (comps_in_scan == 1) block_y_mcu[comp_list[0]]++; else { for (component_num = 0; component_num < comps_in_scan; component_num++) { component_id = comp_list[component_num]; block_y_mcu[component_id] += comp_v_samp[component_id]; } } } } //------------------------------------------------------------------------------ // Decode a progressively encoded image. void jpeg_decoder::init_progressive(void) { int i; if (comps_in_frame == 4) terminate(JPGD_UNSUPPORTED_COLORSPACE); // Allocate the coefficient buffers. for (i = 0; i < comps_in_frame; i++) { dc_coeffs[i] = coeff_buf_open(max_mcus_per_row * comp_h_samp[i], max_mcus_per_col * comp_v_samp[i], 1, 1); ac_coeffs[i] = coeff_buf_open(max_mcus_per_row * comp_h_samp[i], max_mcus_per_col * comp_v_samp[i], 8, 8); } for ( ; ; ) { int dc_only_scan, refinement_scan; Pdecode_block_func decode_block_func; if (!init_scan()) break; dc_only_scan = (spectral_start == 0); refinement_scan = (successive_high != 0); if ((spectral_start > spectral_end) || (spectral_end > 63)) terminate(JPGD_BAD_SOS_SPECTRAL); if (dc_only_scan) { if (spectral_end) terminate(JPGD_BAD_SOS_SPECTRAL); } else if (comps_in_scan != 1) /* AC scans can only contain one component */ terminate(JPGD_BAD_SOS_SPECTRAL); if ((refinement_scan) && (successive_low != successive_high - 1)) terminate(JPGD_BAD_SOS_SUCCESSIVE); if (dc_only_scan) { if (refinement_scan) decode_block_func = progressive_block_decoder::decode_block_dc_refine; else decode_block_func = progressive_block_decoder::decode_block_dc_first; } else { if (refinement_scan) decode_block_func = progressive_block_decoder::decode_block_ac_refine; else decode_block_func = progressive_block_decoder::decode_block_ac_first; } decode_scan(decode_block_func); //get_bits_2(bits_left & 7); bits_left = 16; //bit_buf = 0; get_bits_1(16); get_bits_1(16); } comps_in_scan = comps_in_frame; for (i = 0; i < comps_in_frame; i++) comp_list[i] = i; calc_mcu_block_order(); } //------------------------------------------------------------------------------ void jpeg_decoder::init_sequential(void) { if (!init_scan()) terminate(JPGD_UNEXPECTED_MARKER); } //------------------------------------------------------------------------------ void jpeg_decoder::decode_start(void) { init_frame(); if (progressive_flag) init_progressive(); else init_sequential(); } //------------------------------------------------------------------------------ // Find the start of the JPEG file and reads enough data to determine // its size, number of components, etc. void jpeg_decoder::decode_init(Pjpeg_decoder_stream Pstream, bool use_mmx) { init(Pstream, use_mmx); locate_sof_marker(); } //------------------------------------------------------------------------------ // Call get_error_code() after constructing to determine if the stream // was valid or not. You may call the get_width(), get_height(), etc. // methods after the constructor is called. // You may then either destruct the object, or begin decoding the image // by calling begin(), then decode(). jpeg_decoder::jpeg_decoder(Pjpeg_decoder_stream Pstream, bool use_mmx) { if (setjmp(jmp_state)) return; decode_init(Pstream, use_mmx); } //------------------------------------------------------------------------------ // If you wish to decompress the image, call this method after constructing // the object. If JPGD_OKAY is returned you may then call decode() to // fetch the scan lines. int jpeg_decoder::begin(void) { if (ready_flag) return (JPGD_OKAY); if (error_code) return (JPGD_FAILED); if (setjmp(jmp_state)) return (JPGD_FAILED); decode_start(); ready_flag = true; return (JPGD_OKAY); } //------------------------------------------------------------------------------ // Completely destroys the decoder object. May be called at any time. jpeg_decoder::~jpeg_decoder() { free_all_blocks(); } //------------------------------------------------------------------------------ // // New i/f to JPEG decoding for CLIP pictures. // Now using Rich Geldeich's library, and this function is // based partly on... // //------------------------------------------------------------------------------ // jpg2tga.cpp // JPEG to TGA file conversion example program. // Last updated: Nov. 16, 2000 // Copyright (C) 1994-2000 Rich Geldreich // richgel@voicenet.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //------------------------------------------------------------------------------ int clip_jpeg_decode(unsigned char ***components, const char *filename, int& rows, int& cols, int& pnmtype) { Pjpeg_decoder_file_stream Pinput_stream = new jpeg_decoder_file_stream(); if (Pinput_stream->open(filename)) { delete Pinput_stream; return -1; } Pjpeg_decoder Pd = new jpeg_decoder(Pinput_stream, 0); if (Pd->get_error_code() != 0) { // printf("Error: Decoder failed! Error status: %i\n", Pd->get_error_code()); // Always be sure to delete the input stream object _after_ // the decoder is deleted. Reason: the decoder object calls the input // stream's detach() method. delete Pd; delete Pinput_stream; return -1; } cols = Pd->get_width(); rows = Pd->get_height(); pnmtype = Pd->get_num_components(); if (Pd->begin()) { // printf("Error: Decoder failed! Error status: %i\n", Pd->get_error_code()); delete Pd; delete Pinput_stream; return -1; } for (int compnum = 0; compnum < pnmtype; compnum++) { components[compnum] = new unsigned char *[rows]; unsigned char **q = components[compnum]; for (int j = 0; j < rows; j++) q[j] = new unsigned char [cols]; } int lines_decoded = 0; // printf("Bytes per pel= %d\n", Pd->get_bytes_per_pixel()); for (int i = 0; i < rows; i++) { void *Pscan_line_ofs; unsigned int scan_line_len; if (Pd->decode(&Pscan_line_ofs, &scan_line_len)) break; lines_decoded++; unsigned char *p = (unsigned char *) Pscan_line_ofs; if (pnmtype == 1) { for (int j = 0; j < cols; j++) components[0][i][j] = *p++; } else { for (int j = 0; j < cols; j++) { // Uses order GRB components[1][i][j] = p[0]; components[0][i][j] = p[1]; components[2][i][j] = p[2]; p += Pd->get_bytes_per_pixel(); } } } if (Pd->get_error_code()) { // printf("Error: Decoder failed! Error status: %i\n", Pd->get_error_code()); delete Pd; delete Pinput_stream; return -1; } // printf("Lines decoded: %i\n", lines_decoded); // printf("Input file size: %i\n", Pinput_stream->get_size()); // printf("Input bytes actually read: %i\n", Pd->get_total_bytes_read()); delete Pd; delete Pinput_stream; return 0; } void clip_jpeg_finish(unsigned char ***components, int rows, int cols, int pnmtype){ for (int compnum = 0; compnum < pnmtype; compnum++) { unsigned char **out = components[compnum]; for (int i = 0; i < rows; i++) delete [] out[i]; delete [] out; } } } // namespace clip_internal /* CLIP_H */ // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // The publicly usable functions namespace apt1_0 { int apt_decode(unsigned char ***components, FILE *cfp, int& rows, int& cols, int& pnmtype, int& topmaxval, char **comment, int &bytesperpel); void apt_finish(unsigned char ***components, int truerows, int truecols, int pnmtype); int apt_encode(unsigned char ***incomponents, FILE *cfp, int rows, int cols, int pnmtype, int quality, const char *comment, const int numbits); }; // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // Declarations (and some definitions) of internals. // #define DEBUG 1 // The first section is for the compactor classes namespace apt1_0 { // #define DEBUG 1 // #define NOCOMPACTORDEBUG 1 const int TABLEBITS = 12; const int MASK = (1<> (valid-bits)) & mask); } void skipbits(const int bits) { valid -= bits; } int get(unsigned char *declength, unsigned char *decode, register int bits, const int mask) { register int v = valid; if (v < bits) { loadforlook(); if ((v += 8) < bits) { loadforlook(); v += 8; } } bits = (int) ((bitbuf >> (v-bits)) & mask); valid = v - declength[bits]; return decode[bits]; } public: cfileio(FILE *file) { cfp = file; outbyte = 0; outbits = 0; bitbuf = 0; charsleft = 0; valid = 0; } void putbit(int val) { outbyte <<= 1; if (val) outbyte++; outbits++; if ((outbits &= 0x7) == 0) putc(outbyte, cfp); } void putbits(int val, int numbits) { if (!numbits) return; int space = 8-outbits; if (numbits > space) { outbyte <<= space; outbyte |= val>>(numbits = numbits-space); putc(outbyte, cfp); outbits = 0; space = 8; val &= (1< space) { // Now space = 8 and outbyte is empty outbyte = val>>(numbits = numbits-space); putc(outbyte, cfp); val &= (1<> outbits); putc(outbyte, cfp); outbyte = val; } void flush() { if (outbits) { outbyte <<= 8 - outbits; putc(outbyte ,cfp); outbits = 0; } fflush(cfp); } int getbit() { if (!valid) { bitbuf = (charsleft-- ? *pbuf++ : (fillbuf() && charsleft--) ? *pbuf++ : 0); valid = 8; } return (int) ((bitbuf >> --valid) & 1); } int getbits(int numbits) { // numbits <= 8 int mask = (1<>3); fseek(cfp, -charsleft-(valid>>3), SEEK_CUR); charsleft = 0; valid = 0; return retval; } ~cfileio() {} friend class compactor; }; inline void compactor::putbit(int val) { #ifdef DEBUG if (debug) printf("Coder %d: bit %d\n",id,val); #endif pfio->putbit(val); } inline int compactor::getbit() { int code = pfio->getbit(); #ifdef DEBUG if (debug) printf("Coder %d: bit %d\n",id,code); #endif return code; } inline int compactor::get () { int code; if (!usinghuffman) { code = pfio->getbits(litlength); #ifdef DEBUG if (debug) { printf("Coder %d: uncoded symbol %d (0x%x)\n",id,code,code); fflush(stdout); } #endif return code; } code = pfio->get(declength, decode, longest, longmask); #ifdef DEBUG if (debug) { printf("Coder %d: symbol %d (0x%x)\n",id,code,code); fflush(stdout); } #endif return code; } const int FLAGWORDCHUNK = 3; const int FLAGWORDS = 3*3*3; const int MAXFLAG = 63; // Runlength/huffman compression class class rlcomp { private: int debug; int id; int usingflagwords; // Flag to say whether using flagwords int symbolcnt; int doublecode; // Two successive flagwords of all freqsymbol unsigned char freqsymbol; unsigned char secondfreqsymbol; unsigned char decodeflagword; unsigned char *inbuf; unsigned char *procbuf; unsigned char *readproc; unsigned char *inbufend; unsigned char *procbufend; int buflength; compactor *psymbols; compactor *pflagwords; long freq[256]; void disable_compactor_debug() { if (!debug) return; psymbols->debugoff(); pflagwords->debugoff(); } void enable_compactor_debug() { if (!debug) return; psymbols->debugon(); pflagwords->debugon(); } public: rlcomp(); ~rlcomp(); int encodelength(int maxlength); // Called to set up encode buffers void putbit(int val) { psymbols->putbit(val);} int getbit() { return psymbols->getbit();} void reset(); void debugon() { debug=1; psymbols->debugon(); pflagwords->debugon();} void debugoff() { debug=0; psymbols->debugoff(); pflagwords->debugoff();} void indexnumber(int i) { id = i; psymbols->indexnumber(i+100); pflagwords->indexnumber(i+200); } void attach(class cfileio *f) { psymbols->attach(f); pflagwords->attach(f); } void inc(int symbol) { freq[symbol]++; *inbufend++ = (unsigned char) symbol; } void buildtree(void); void writetree(void); void readtree(void); void maketable_encode(void); void maketable_decode(void); void incorput(int symbol, int which); void put(int inbyte); int get(){ if (!usingflagwords) return psymbols->get(); if (!symbolcnt) { if (doublecode) { doublecode--; decodeflagword = FLAGWORDS - 1; } else { decodeflagword = pflagwords->get(); if (decodeflagword >= FLAGWORDS) { doublecode = decodeflagword+1 -FLAGWORDS; decodeflagword = FLAGWORDS - 1; } } } else decodeflagword *= 3; if (++symbolcnt == FLAGWORDCHUNK) symbolcnt = 0; int temp = decodeflagword/(FLAGWORDS/3); temp %= 3; if (temp == 2) return freqsymbol; if (temp) return secondfreqsymbol; return psymbols->get(); } }; const int numcoders = 8; const int SST = 0; // First numbered coder for square band const int DST = 4; // First numbered coder for diamond band inline int get_ridgethresh(int spacing) { // return (int)(10.0*log((double)spacing)); // math-library-free approx to above: int ret = 2*spacing + 5; if (ret > 16) ret = 16 + (ret-16)/2; if (ret > 32) ret = 32 + (ret-32)/8; if (ret < 8) // Spacing = 1 ret = 1; return ret; } int get_numlevels(); int makescratch(const int& xmax, const int& ymax); int deletescratch(const int& ymax); void enc_plane(unsigned char **in, const int& xmax, const int& ymax, const int& fromlevel, const int& tolevel, const int *spacing, rlcomp *codeout, const int& minval, const int& maxval, const int& comp, const int& truecols, const int& truerows); int bincode(unsigned char ***pyr, const int& inrows, const int& incols, rlcomp *codeout, int fromlevel, const int *spacing, const int pnmtype, const int& truerows, const int& truecols); void calc_colour_transform(unsigned char ***components, int *transform, int num_rows, int num_cols); void rgb_pca(unsigned char ***components, int *transform, int num_rows, int num_cols); int calcq(int dynamicrange, int quality, int *qvals, int numbands); void gettrueminmax(unsigned char ***components, const int pnmtype, int *minval, int *maxval, const int& rows, const int& cols); void pad(unsigned char ***incomponents, unsigned char ***pbuf, int& rows, int& cols, int pnmtype); const unsigned char **initclip(const int& bottomval, const int& topval, int pnmtype); void dec_plane(unsigned char ***out,const int& XMAX, const int& YMAX, const int& fromlevel, const int *spacing, rlcomp *codein, const int *minval, const int *maxval, const int pnmtype, const int& truecols, const int& truerows); void pca_rgb(unsigned char ***components, int *transform, int num_rows, int num_cols, const unsigned char **clip); int read_header(FILE *cfp, int& pnmtype, int& rows, int& cols, int *minval, int *maxval, int *tminval, int *tmaxval, int& numlevels, char& coltype, int *spacing, int *transform, char **comment); }; // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // // Order of values to predictor mapping table. // namespace apt1_0 { static const int orderarray[81] = { // SHAPE HIGHEST LOWEST NOMINAL 0, // ---- impossible 0, // ---0 impossible 7, // ---+ twist edge P S (Q+R)/2 0, // --0- impossible 0, // --00 impossible 1, // --0+ twist edge P R=S (Q+S)/2 10, // --+- twist edge S R (P+Q)/2 1, // --+0 twist edge P=S R (Q+S)/2 1, // --++ edge P R (Q+S)/2 0, // -0-- impossible 0, // -0-0 impossible 12, // -0-+ hard-twist P S R(=Q) 0, // -00- impossible 0, // -000 impossible 12, // -00+ isolate up P Q=R=S R 2, // -0+- twist edge S Q=R (P+R)/2 3, // -0+0 Vert edge P=S Q=R SHAPE_V_EST 8, // -0++ twist edge P Q=R (R+S)/2 9, // -+-- twist edge R Q (P+S)/2 11, // -+-0 hard-twist R Q P (=S) 6, // -+-+ line P or R Q or S Flag 2, // -+0- twist edge R=S Q (P+R)/2 14, // -+00 isol down P=R=S Q S 12, // -+0+ hard-twist P Q R (=S) 2, // -++- edge S Q (P+R)/2 2, // -++0 twist edge P=S Q (P+R)/2 8, // -+++ twist edge P Q (R+S)/2 0, // 0--- impossible 0, // 0--0 impossible 2, // 0--+ twist edge P=Q S (P+R)/2 0, // 0-0- impossible 0, // 0-00 impossible 4, // 0-0+ Horiz edge P=S R=Q SHAPE_H_EST 11, // 0-+- hard-twist S R P (=Q) 11, // 0-+0 isol down P=Q=S R P 1, // 0-++ twist edge P=Q R (Q+S)/2 0, // 00-- impossible 0, // 00-0 impossible 13, // 00-+ isol down P=Q=R S Q 0, // 000- impossible 5, // 0000 Flat P=Q=R=S H_ESTorV_EST 0, // 000+ impossible 13, // 00+- isol up S P=Q=R Q 0, // 00+0 impossible 0, // 00++ impossible 1, // 0+-- twist edge R P=Q (Q+S)/2 11, // 0+-0 isol up R P=Q=S P 11, // 0+-+ hard-twist R S P (=Q) 4, // 0+0- Horiz edge R=Q P=S SHAPE_H_EST 0, // 0+00 impossible 0, // 0+0+ impossible 2, // 0++- twist edge S P=Q (P+R)/2 0, // 0++0 impossible 0, // 0+++ impossible 8, // +--- twist edge Q P (R+S)/2 2, // +--0 twist edge Q P=S (P+R)/2 2, // +--+ edge Q S (P+R)/2 12, // +-0- hard-twist Q P R (=S) 12, // +-00 isol up Q P=R=S S 2, // +-0+ twist edge Q R=S (P+R)/2 6, // +-+- line Q or S P or R Flag 11, // +-+0 hard-twist Q R P (=S) 9, // +-++ twist edge Q R (P+S)/2 8, // +0-- twist edge Q=R P (R+S)/2 3, // +0-0 Vert edge Q=R P=S SHAPE_V_EST 2, // +0-+ twist edge Q=R S (P+R)/2 12, // +00- isol down Q=R=S P R 0, // +000 impossible 0, // +00+ impossible 12, // +0+- hard-twist S P R (=Q) 0, // +0+0 impossible 0, // +0++ impossible 1, // ++-- edge R P (Q+S)/2 1, // ++-0 twist edge R P=S (Q+S)/2 10, // ++-+ twist edge R S (P+Q)/2 1, // ++0- twist edge R=S P (Q+S)/2 0, // ++00 impossible 0, // ++0+ impossible 7, // +++- twist edge S P (Q+R)/2 0, // +++0 impossible 0, // ++++ impossible }; }; // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // Order statistics prefilter for lossy encoding namespace apt1_0 { void order_filter(unsigned char ***components, int nr, int nc, int numcomps, int quality) { // 3x3 modified median filter. Messiness is in output buffering unsigned char *outbuf1 = new unsigned char[nc]; unsigned char *outbuf2 = new unsigned char[nc]; unsigned char sortarray[9]; unsigned char sortarray2[9]; int ovrange = 90 - quality; if (ovrange > 80) ovrange = 80; for (int compnum = 0; compnum < numcomps; compnum++) { int range = ovrange; if (compnum) range = ovrange*2; unsigned char *prevout = outbuf1; unsigned char *currout = outbuf2; unsigned char **pic = components[compnum]; // Initialize first "previous" line memcpy(prevout, pic[0], nc); for (int i = 1; i < nr-1; i++) { // Initialize first and last points of current line currout[0] = pic[i][0]; currout[nc-1] = pic[i][nc-1]; for (int j = 1; j < nc-1; j++) { // Clunky for now, just to get working int maxindex = 0; int minindex = 0; int minval, maxval; int curr; minval = maxval = sortarray[0] = pic[i-1][j-1]; curr = sortarray[1] = pic[i-1][j]; if (curr > maxval) {maxval=curr; maxindex=1;} else if (curr maxval) {maxval=curr; maxindex=2;} else if (curr maxval) {maxval=curr; maxindex=3;} else if (curr maxval) {maxval=curr; maxindex=4;} else if (curr maxval) {maxval=curr; maxindex=5;} else if (curr maxval) {maxval=curr; maxindex=6;} else if (curr maxval) {maxval=curr; maxindex=7;} else if (curr maxval) {maxval=curr; maxindex=8;} else if (curr range) { currout[j] = pic[i][j]; continue; } int ii, jj; for (ii = 0, jj = 0; ii < 9; ii++) { if ((ii == minindex)||(ii == maxindex)) continue; sortarray2[jj++] = sortarray[ii]; } minindex = maxindex = 0; minval = maxval = sortarray2[0]; curr = sortarray2[1]; if (curr > maxval) {maxval=curr; maxindex=1;} else if (curr maxval) {maxval=curr; maxindex=2;} else if (curr maxval) {maxval=curr; maxindex=3;} else if (curr maxval) {maxval=curr; maxindex=4;} else if (curr maxval) {maxval=curr; maxindex=5;} else if (curr maxval) {maxval=curr; maxindex=6;} else if (curr maxval) {maxval=curr; maxindex=1;} else if (curr maxval) {maxval=curr; maxindex=2;} else if (curr maxval) {maxval=curr; maxindex=3;} else if (curr maxval) {maxval=curr; maxindex=4;} else if (curr maxval) currout[j] = maxval; else if (pic[i][j] < minval) currout[j] = minval; else currout[j] = pic[i][j]; continue; } for (ii = 0, jj = 0; ii < 5; ii++) { if ((ii == minindex)||(ii == maxindex)) continue; sortarray2[jj++] = sortarray[ii]; } minindex = maxindex = 0; minval = maxval = sortarray2[0]; curr = sortarray2[1]; if (curr > maxval) {maxval=curr; maxindex=1;} else if (curr maxval) {maxval=curr; maxindex=2;} else if (curr maxval) currout[j] = maxval; else if (pic[i][j] < minval) currout[j] = minval; else currout[j] = pic[i][j]; } // At the end of the line we can write the previous // line, because won't be used for input again memcpy(pic[i-1],prevout,nc); if (prevout == outbuf1) { prevout = outbuf2; currout = outbuf1; } else { prevout = outbuf1; currout = outbuf2; } } // Write out the last line memcpy(pic[nr-2],prevout,nc); } delete [] outbuf1; delete [] outbuf2; } }; // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // Functions for lossless palettization. // The maximum palette supported here is 4096. The palettization algorithm is // order N^3, so when the number of entries reaches this maximum, encoding // time can be long. // // The palettization algorithm is adapted from that proposed in // W Zeng, J Li, S Lei, "An Efficient Color Re-indexing Scheme for // Palette-Based Compression", ICIP 2000. Warning: This scheme may be // patented! The palettization method is therefore not a part of APT and // is included as an illustration of how palettization may be done. // I.e. APT supports the palettization of pictures and defines how the // palette is encoded, but not how the palette is ordered. namespace apt1_0 { /* // // Temp stuff for debugging // int writepgm(const char *pgmname, unsigned char **pbuf, const int& rows, const int& cols, const int& maxval) // Write picture to "P5" pgm format { FILE *out = fopen(pgmname, "w"); if (!out) { fprintf(stderr,"Cannot open %s\n", pgmname); return -1; } fprintf(out,"P5\n%d %d\n%d\n", cols, rows, maxval); for (int i = 0; i < rows; i++) fwrite(pbuf[i],1,cols,out); fclose(out); return 0; } // // End of temp stuff for debugging // */ struct entry { unsigned char g; unsigned char r; unsigned char b; int valsum; int cnt; int index; entry *hist_next; entry *pool_prev; entry *pool_next; bool whichpool; }; class palettize { entry palette[4097]; // Extra one so can do pool-stepping without // memory error double weightsarray[4096]; int totalentries; int paddedentries; // Next highest multiple of 256 greater than // or equal to totalentries. int startindex; // Offset of start of palette. int palettebits; int palettemask; entry unsorted; // unsorted pool starts here, but this actual entry // is a dummy. entry unsorted_end; // pool_prev points to last in unsorted pool entry sorted; // sorted pool. This actual entry is a dummy. entry sorted_end; // pool_prev pointer points to last in pool int **cooccur; // cooccurence matrix int *crosscounts; // crosscount sums void initpools() { unsorted.pool_next = palette; entry *pe = palette; pe->pool_prev = &unsorted; int i; for (i = 0; i < totalentries; i++) { pe->pool_next = (pe+1); pe->whichpool = 0; pe++; pe->pool_prev = (pe-1); } pe--; pe->pool_next = &unsorted_end; unsorted_end.pool_prev = pe; // Put first two entries in sorted pool int firsti = 0; int secondi = 0; int firstival = 0; int secondival = 0; for (i = 0; i < totalentries; i++) { if (crosscounts[i] > firstival) { firsti = i; firstival = crosscounts[i]; } } for (i = 0; i < totalentries; i++) { if (i == firsti) continue; if (cooccur[firsti][i] > secondival) { secondi = i; secondival = cooccur[firsti][i]; } } // printf("firsti = %d, secondi = %d\n",firsti,secondi); entry *pfirst = &palette[firsti]; entry *psecond = &palette[secondi]; // Unlink from unsorted pool entry *pprev = pfirst->pool_prev; entry *pnext = pfirst->pool_next; pprev->pool_next = pnext; pnext->pool_prev = pprev; pfirst->whichpool = 1; pprev = psecond->pool_prev; pnext = psecond->pool_next; pprev->pool_next = pnext; pnext->pool_prev = pprev; psecond->whichpool = 1; // Put as first and second members of sorted pool sorted.pool_next = pfirst; pfirst->pool_next = psecond; psecond->pool_next = &sorted_end; sorted_end.pool_prev = psecond; psecond->pool_prev = pfirst; pfirst->pool_prev = &sorted; } int tosorted(int index, bool frontnotback) { entry *pe = &palette[index]; if (pe->whichpool) // Already on sorted list return -1; entry *pprev = pe->pool_prev; entry *pnext = pe->pool_next; // Unlink from unsorted pool pprev->pool_next = pnext; pnext->pool_prev = pprev; pe->whichpool = 1; // Link to front or back or sorted pool if (frontnotback) { pe->pool_next = sorted.pool_next; sorted.pool_next = pe; pe->pool_next->pool_prev = pe; pe->pool_prev = &sorted; } else { pe->pool_prev = sorted_end.pool_prev; sorted_end.pool_prev = pe; pe->pool_prev->pool_next = pe; pe->pool_next = &sorted_end; } return 0; } void createcoocur(unsigned char ***components, int nr, int nc, entry *hist[256][256]) { cooccur = new int *[totalentries]; int i; for (i = 0; i < totalentries; i++) cooccur[i] = new int[totalentries]; crosscounts = new int[totalentries]; for (i = 0; i < totalentries; i++) for (int j = 0; j < totalentries; j++) cooccur[i][j] = 0; for (i = 0; i < totalentries; i++) crosscounts[i] = 0; // Just do 1D for now for (i = 0; i < nr; i++) { unsigned char *g = components[0][i]; unsigned char *r = components[1][i]; unsigned char *b = components[2][i]; int prev = -1; // To skip first round for (int j = 0; j < nc; j++) { entry *p = hist[*g][*r]; while (p) { if (p->b == *b) break; p = p->hist_next; } g++; r++; b++; if (prev >= 0) { cooccur[prev][p->index]++; cooccur[p->index][prev]++; } prev = p->index; } } for (i = 0; i < totalentries; i++) for (int j = 0; j < totalentries; j++) { if (j == i) continue; crosscounts[i] += cooccur[i][j]; } } void deletecooccur() { for (int i = 0; i < totalentries; i++) delete [] cooccur[i]; delete [] cooccur; delete [] crosscounts; } void initweights() { double log2 = log(2.0); for (int i = 0; i < totalentries; i++) { weightsarray[i] = log(1.0+1.0/(1+(double)i)); weightsarray[i] /= log2; // Necessary? } } int move_next_to_sorted_pool() { entry *src = unsorted.pool_next; if (src == &unsorted_end) return 0; entry *bestleft = src; entry *bestright = src; double bestleftval = 0; double bestrightval = 0; int indexarray[4096]; int numsorted = 0; // Set up indices from sorted pool entry *p = sorted.pool_next; while (p != &sorted_end) { indexarray[numsorted++] = p->index; p = p->pool_next; } while (src != &unsorted_end) { // Main loop double left = 0; double right = 0; for (int i = 0; i < numsorted; i++) { double curr = (double) cooccur[src->index][indexarray[i]]; left += curr*weightsarray[i]; right += curr*weightsarray[numsorted-1-i]; } if (left > bestleftval) { bestleftval = left; bestleft = src; } if (right > bestrightval) { bestrightval = right; bestright = src; } src = src->pool_next; } if (bestrightval > bestleftval) tosorted(bestright->index,0); else tosorted(bestleft->index,1); return 1; } /* // Debug function void print_pools() { entry *pe = unsorted.pool_next; printf("Unsorted pool:\n"); while (pe != &unsorted_end) { printf("\tIndex %d: (%d,%d,%d), cnt = %d\n", pe->index, pe->r, pe->g, pe->b, pe->cnt); pe = pe->pool_next; } pe = sorted.pool_next; int cnt = 0; printf("Sorted pool:\n"); while (pe != &sorted_end) { printf("\t (%d) Index %d: (%d,%d,%d), cnt = %d\n", cnt, pe->index, pe->r, pe->g, pe->b, pe->cnt); pe = pe->pool_next; cnt++; } } */ public: // Most of the actual palettization is done in the private functions. // They are called by the constructor below. palettize(unsigned char ***components, int nr, int nc, int quality, bool& cando) { int maxentries = 10; if (quality > 70) maxentries = 256; if ((quality == 100)&&(nr*nc > 512*512)) // Size criterion is because palette will take significant // extra space. Over a broad range around 0.25Mbytes, graphics // are about equally well coded with and without palettes. maxentries = 4096 - 256; entry *histogram[256][256]; int i, j; for (i = 0; i < 256; i++) for (j = 0; j < 256; j++) histogram[i][j] = 0; for (i = 0; i < maxentries; i++) palette[i].cnt = 0; for (i = 0; i < nr; i++) { unsigned char *g = components[0][i]; unsigned char *r = components[1][i]; unsigned char *b = components[2][i]; for (j = 0; j < nc; j++) { entry *p = histogram[*g][*r]; while(p) { if (p->b == *b) { p->cnt++; break; } p = p->hist_next; } if (!p) { entry *q = new entry; q->g = *g; q->r = *r; q->b = *b; q->cnt = 1; q->hist_next = histogram[*g][*r]; histogram[*g][*r] = q; } g++; r++; b++; } } totalentries = 0; int paletteindex = 0; for (i = 0; i < 256; i++) { for (j = 0; j < 256; j++) { int numentries = 0; entry *p = histogram[i][j]; while(p) { numentries++; p = p->hist_next; } totalentries += numentries; if (totalentries > maxentries) { // printf("Too many to palettize\n"); cando = 0; return; } p = histogram[i][j]; while(p) { p->index = paletteindex; palette[paletteindex] = *p; palette[paletteindex++].hist_next = p; // In the palette the hist_next pointers // point back to the histogram p = p->hist_next; } } } // printf("Palette size = %d\n", totalentries); palettebits = 0; while (totalentries > (1<>8)<<8; if (totalentries > paddedentries) { paddedentries += 256; if (!(paddedentries & 0x100)) // Don't want even number paddedentries = totalentries; // of 256-entry blocks } cando = 1; // Reorganise palette for more efficient coding createcoocur(components, nr, nc, histogram); initpools(); // print_pools(); initweights(); while(move_next_to_sorted_pool()); // print_pools(); deletecooccur(); // So now the ordering algorithm is done, we just have // to get the data structures right. // Work out the start index startindex = (paddedentries - totalentries)/2; // For now // Reorder index values in the palette and copy in order // to temp palette entry *temp = new entry[paddedentries]; entry *pe = sorted.pool_next; int newindex = startindex; while(pe != &sorted_end) { pe->index = newindex; temp[newindex++] = *pe; pe = pe->pool_next; } // Overwrite palette with ordered palette // (pointers will no longer be valid) for (i = startindex; i < totalentries+startindex; i++) palette[i] = temp[i]; delete [] temp; // Reorder histogram index values; pe = palette+startindex; for (i = 0; i < totalentries; i++) { pe->hist_next->index = pe->index; pe++; } /* printf("First palette value = %d,%d,%d. cnt = %d\n",palette[0].r,palette[0].g, palette[0].b, palette[0].cnt); printf("255 palette value = %d,%d,%d. cnt = %d\n",palette[255].r,palette[255].g, palette[255].b, palette[255].cnt); printf("256 palette value = %d,%d,%d. cnt = %d\n",palette[256].r,palette[256].g, palette[256].b, palette[256].cnt); printf("Last palette value = %d,%d,%d. cnt = %d\n",palette[totalentries-1].r,palette[totalentries-1].g, palette[totalentries-1].b, palette[totalentries-1].cnt); */ // Now actually do the palette mapping for (i = 0; i < nr; i++) { unsigned char *g = components[0][i]; unsigned char *r = components[1][i]; unsigned char *b = components[2][i]; for (j = 0; j < nc; j++) { entry *p = histogram[*g][*r]; while(p) { if (p->b == *b) { break; } p = p->hist_next; } if (!p) { printf("Internal error in palettize\n"); return; } if (palettebits > 8) { // *g = ((p->index>>(palettebits-8)))&0xff; // *r = p->index & (palettemask>>8); // Note there is one last trick in here. We // "ripple" the coding of the least significant // eight bits so that boundaries between // 256-entry blocks are not abrupt. int topbits = *r = (p->index>>8)&0xff; if (topbits & 1) *g = 255 - (p->index&0xff); else *g = p->index&0xff; } else { *g = p->index; *r = 0; } *b = 0; g++; r++; b++; } } /* // Debug stuff writepgm("palpic1", components[0], nr, nc, 255); if (palettebits > 8) writepgm("palpic2", components[1], nr, nc, 15); */ // Clean up for (i = 0; i < 256; i++) for (j = 0; j < 256; j++) { entry *p = histogram[i][j]; entry *q; while(p) { q = p->hist_next; delete p; p = q; } } } ~palettize() { } // Decoder calls frompalette to restore actual values back int frompalette(unsigned char ***components, int nr, int nc) { int i, j; for (i = 0; i < nr; i++) { unsigned char *g = components[0][i]; unsigned char *r = components[1][i]; unsigned char *b = components[2][i]; for (j = 0; j < nc; j++) { int index = *g; if (palettebits > 8) { // Reverse the ripple! int topbits = *r; if (topbits & 1) index = 255 - index; index |= (topbits << 8); } // index = (index<<(palettebits-8))+*r; *g++ = palette[index].g; *r++ = palette[index].r; *b++ = palette[index].b; } } return 0; } void writepalette(FILE *cfp, cfileio& fio) { rlcomp codeout; codeout.encodelength(1024*64); codeout.attach(&fio); codeout.indexnumber(0); // codeout.debugon(); fputc((unsigned char) ((totalentries>>8)&0xff), cfp); fputc((unsigned char) (totalentries&0xff), cfp); fputc((unsigned char) startindex, cfp); /**/ unsigned char prevval = 0; int i; for (i = startindex; i < totalentries+startindex; i++) { palette[i].g = palette[i].g - prevval; prevval = palette[i].g + prevval; } prevval = 0; for (i = startindex; i < totalentries+startindex; i++) { palette[i].r = palette[i].r - prevval; prevval = palette[i].r + prevval; } prevval = 0; for (i = startindex; i < totalentries+startindex; i++) { palette[i].b = palette[i].b - prevval; prevval = palette[i].b + prevval; } /**/ for (i = startindex; i < totalentries+startindex; i++) codeout.inc(palette[i].g); codeout.writetree(); for (i = startindex; i < totalentries+startindex; i++) codeout.put(palette[i].g); codeout.reset(); for (i = startindex; i < totalentries+startindex; i++) codeout.inc(palette[i].r); codeout.writetree(); for (i = startindex; i < totalentries+startindex; i++) codeout.put(palette[i].r); codeout.reset(); for (i = startindex; i < totalentries+startindex; i++) codeout.inc(palette[i].b); codeout.writetree(); for (i = startindex; i < totalentries+startindex; i++) codeout.put(palette[i].b); fio.flush(); } // Constructor below is used by decoder and reads the palette in palettize(FILE *cfp, cfileio& fio) { rlcomp codein; codein.attach(&fio); codein.indexnumber(0); // codein.debugon(); unsigned char topbyte, bottombyte; topbyte = fgetc(cfp); bottombyte = fgetc(cfp); startindex = fgetc(cfp); totalentries = (topbyte<<8) + bottombyte; // totalentries = fgetc(cfp); // if (!totalentries) // totalentries = 256; // printf("Palette size = %d\n", totalentries); palettebits = 0; while (totalentries > (1<>8)<<8; if (totalentries > paddedentries) { paddedentries += 256; if (!(paddedentries & 0x100)) // Don't want even number paddedentries = totalentries; // of 256-entry blocks } codein.readtree(); int i; for (i = startindex; i < totalentries+startindex; i++) palette[i].g = codein.get(); codein.readtree(); for (i = startindex; i < totalentries+startindex; i++) palette[i].r = codein.get(); codein.readtree(); for (i = startindex; i < totalentries+startindex; i++) palette[i].b = codein.get(); fio.rewind(); /**/ unsigned char prevval = 0; for (i = startindex; i < totalentries+startindex; i++) prevval = palette[i].g = palette[i].g + prevval; prevval = 0; for (i = startindex; i < totalentries+startindex; i++) prevval = palette[i].r = palette[i].r + prevval; prevval = 0; for (i = startindex; i < totalentries+startindex; i++) prevval = palette[i].b = palette[i].b + prevval; /**/ } }; class bwpalettize { unsigned char palette[10]; int reversepalette[256]; unsigned char *p; int totalentries; int palettebits; int palettemask; public: bwpalettize(unsigned char ***components, int nr, int nc, int quality, bool& cando) { int i, j; totalentries = 0; for (i = 0; i < 256; i++) reversepalette[i] = -1; for (i = 0; i < nr; i++) { p = components[0][i]; for (j = 0; j < nc; j++) { unsigned char pel = *p++; if (reversepalette[pel] < 0) { if (totalentries == 10) { cando = 0; return; } reversepalette[pel] = totalentries; palette[totalentries++] = pel; } } } // printf("Palette size = %d\n", totalentries); palettebits = 0; while (totalentries > (1< (1<>shiftbits]; *pr++=clip1[(g_to_r[g]+r_to_r[r]+b_to_r[b]+(1<<(shiftbits-1)))>>shiftbits]; *pb++=clip2[(g_to_b[g]+r_to_b[r]+b_to_b[b]+(1<<(shiftbits-1)))>>shiftbits]; } } } }; // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // // Member functions for the compact data type implement a static Huffman coder. // // using namespace apt1_0; namespace apt1_0 { // #define DEBUG 1 // #define DEBUG2 1 void compactor::reset(void) { #ifdef DEBUG if (debug) printf("\nCoder %d: reset:\n",id); fflush(stdout); #endif for (int i = 0; i < 256; i++) freq[i] = 0; } void compactor::buildtree(int freq[], unsigned char num_symbols_of_length[], unsigned char ordered_symbols[], int& longest) const // Based on JPEG huffman algorithm { #ifdef DEBUG if (debug) printf("\nCoder %d: buildtree:\n",id); fflush(stdout); #endif int i; int h1; int h2; int length[256]; int descendents[256]; long f[256]; for (i = 0; i < 256; i++) { f[i] = freq[i]; length[i] = 0; descendents[i] = -1; } while(1) { h1 = -1; h2 = -1; // Find smallest non-zero frequency for (i = 0; i < 256; i++) { if (f[i]) { if (h1 == -1 || f[i] < f[h1]) { if (h2 == -1 || f[h1] < f[h2]) h2 = h1; h1 = i; } else if (h2 == -1 || f[i] < f[h2]) h2 = i; } } if (h2 == -1) // Found the root break; // Merge the two trees f[h1] += f[h2]; f[h2] = 0; length[h1]++; while (descendents[h1] >= 0) { h1 = descendents[h1]; // Go down chain length[h1]++; } // Put h2 on this chain descendents[h1] = h2; // And go down its chain while (descendents[h1] >= 0) { h1 = descendents[h1]; length[h1]++; } } if (h1 == -1) // Empty tree - create dummy char { longest = 0; num_symbols_of_length[0] = 1; ordered_symbols[0] = 0; return; } if (length[h1] == 0) // one symbol { longest = 0; num_symbols_of_length[0] = 1; ordered_symbols[0] = h1; return; } for (i = 0; i < 32; i++) num_symbols_of_length[i] = 0; for (i = 0; i < 256; i++) { if (length[i]) num_symbols_of_length[length[i]]++; // Assume never have case of 256 equal symbols } // Reduce to TABLEBITS-sized words using JPEG algorithm for (i = 31; i > TABLEBITS; i--) { while(num_symbols_of_length[i]) { int j = i - 2; while (!num_symbols_of_length[j]) j--; num_symbols_of_length[i] -= 2; num_symbols_of_length[i-1]++; num_symbols_of_length[j+1] += 2; num_symbols_of_length[j]--; } } while(num_symbols_of_length[i] == 0) i--; // i is now length of longest codeword longest = i; int k = 0; for (i = 1; i <= 31; i++) { for (int j = 0; j < 256; j++) if (length[j] == i) ordered_symbols[k++] = j; } } // Decide whether to use Huffman coding or not. int compactor::usetree(int omit_literal_table) const { #ifdef DEBUG if (debug) { printf("\nCoder %d: usetree(%d):\n",id, omit_literal_table); fflush(stdout); printf("Total symbols to code = %ld\n", accum); printf("longest = %d\n", longest); fflush(stdout); int k = 0; for (int i = 1; i <= longest; i++) { printf("num of length %d = %d\n",i, num_symbols_of_length[i]); for (int j = 0; j < num_symbols_of_length[i]; j++, k++) printf("%d[n=%ld] ", ordered_symbols[k], freq[ordered_symbols[k]]); printf("\n"); } } #endif int litbits; int retval; int codelength = 0; int totallength; int totallitbits; int tablelength = 5+longest*8; // Because have to write 4 bits in any // case at the start to give length of // literals plus flag to say whether // omitting table, then longest _bytes_ to // say how many codewords there are of // each length up to longest. // Work out the length of literals if (maxval > 127) litbits = 8; else if (maxval > 63) litbits = 7; else if (maxval > 31) litbits = 6; else if (maxval > 15) litbits = 5; else if (maxval > 7) litbits = 4; else if (maxval > 3) litbits = 3; else if (maxval > 1) litbits = 2; else if (maxval > 0) litbits = 1; else litbits = 0; // Then work out the total codelength int k = 0; for (int i = 0; i <= longest; i++) { for (int j = 0; j < num_symbols_of_length[i]; j++) { codelength += i*freq[ordered_symbols[k]]; if (litbits) { k++; if (!omit_literal_table) tablelength += litbits; } } } totallength = codelength + tablelength; totallitbits = litbits*accum; retval = totallitbits - totallength; // -ve means don't use Huffman #ifdef DEBUG if (debug) { printf("Huffman code will code %ld symbols in %d bits (%d bytes).\n", accum, codelength, (codelength+7)/8); printf("To transmit code table required %d bits (%d bytes).\n", tablelength, (tablelength+7)/8); printf("Total bits required (table+code) = %d (%d bytes).\n", totallength, (totallength+7)/8); printf("Average rate = %f bits/symbol\n",(float)totallength/accum); printf("Maximum input symbol value is %d.\n", maxval); printf("Sending %d-bit literals directly requires %ld bits (%ld bytes).\n", litbits, litbits*accum, (litbits*accum+7)/8); if (totallitbits > totallength) printf("++Huffman coding gives saving of %d bits (%d bytes).\n", totallitbits-totallength, (totallitbits-totallength+7)/8); else printf("--Huffman coding adds additional %d bits (%d bytes).\n", totallength-totallitbits, (totallength-totallitbits+7)/8); printf("\n"); } #endif return retval; } void compactor::buildbothtrees() { // New compactor function to build Huffman codes based on actual freqs and // on best match for constraint that increasing input values have increasing // length codewords unsigned char c1_num_symbols_of_length[32]; unsigned char c1_ordered_symbols[256]; int c1_longest; int c1_usetree; unsigned char c2_num_symbols_of_length[32]; unsigned char c2_ordered_symbols[256]; int c2_longest; int c2_usetree; int f[256]; int i; accum = 0; maxval = 0; minval = -1; for (i = 0; i < 256; i++) { f[i] = freq[i]; accum += freq[i]; if (freq[i] > 0) { maxval = i; if (minval < 0) minval = i; } } if (accum < 1) { accum = 1; minval = 0; } buildtree(f,c1_num_symbols_of_length,c1_ordered_symbols,c1_longest); for (i = 0; i < 32; i++) num_symbols_of_length[i] = c1_num_symbols_of_length[i]; for (i = 0; i < 256; i++) ordered_symbols[i] = c1_ordered_symbols[i]; longest = c1_longest; c1_usetree = usetree(0); // Arg 0 means include literal table // Only consider c2 if minval can be represented in 2 bits // Also, if maxval is very high, it's certainly not worth doing this // and we have to avoid minval=0, maxval=255 case which could cause // errors in buildtree. if ((minval > 3)||(maxval > 200)) { if (c1_usetree <= 0) usinghuffman = 0; else usinghuffman = 1; return; } // Now manipulate things for c2 for (i = minval; i <= maxval; i++) f[i] <<= 8; // Multiply each count by 256 for (i = maxval; i >= minval; i--) { // Have to ensure that these counts are in ascending order, // as if the least valued input had happened most times long cnt = f[i]; for (int j = i-1; j >= minval; j--) { if (f[j] <= cnt) f[j] = cnt+1; } } buildtree(f,c2_num_symbols_of_length,c2_ordered_symbols,c2_longest); for (i = 0; i < 32; i++) num_symbols_of_length[i] = c2_num_symbols_of_length[i]; for (i = 0; i < 256; i++) ordered_symbols[i] = c2_ordered_symbols[i]; longest = c2_longest; c2_usetree = usetree(1); // Arg 1 means omit literal table if ((c1_usetree <= 0)&&(c2_usetree <= 0)) usinghuffman = 0; else if (c1_usetree > c2_usetree) { usinghuffman = 1; for (i = 0; i < 32; i++) num_symbols_of_length[i] = c1_num_symbols_of_length[i]; for (i = 0; i < 256; i++) ordered_symbols[i] = c1_ordered_symbols[i]; longest = c1_longest; } else usinghuffman = 2; } // Write the Huffman tree to the output code file void compactor::writetree(void) { buildbothtrees(); #ifdef DEBUG if (debug) { printf("\nCoder %d: writetree:\n",id); fflush(stdout); printf("Total symbols to code = %ld\n", accum); if (usinghuffman == 2) printf("Using Ordered Pseudo-Huffman:\n"); else if (usinghuffman == 1) printf("Using Standard Huffman:\n"); else printf("Not Using Huffman:\n"); } #endif pfio->putbit(usinghuffman); int litbits; int i; if (usinghuffman) { pfio->putbit(longest & 0x8); pfio->putbit(longest & 0x4); pfio->putbit(longest & 0x2); pfio->putbit(longest & 0x1); pfio->putbit(usinghuffman & 0x2); if (usinghuffman == 2) { pfio->putbit(minval & 0x2); pfio->putbit(minval & 0x1); } } // Work out the longest literal if (maxval > 127) litbits = 8; else if (maxval > 63) litbits = 7; else if (maxval > 31) litbits = 6; else if (maxval > 15) litbits = 5; else if (maxval > 7) litbits = 4; else if (maxval > 3) litbits = 3; else if (maxval > 1) litbits = 2; else if (maxval > 0) litbits = 1; else litbits = 0; litlength = litbits; pfio->putbit(litbits & 0x8); pfio->putbit(litbits & 0x4); pfio->putbit(litbits & 0x2); pfio->putbit(litbits & 0x1); #ifdef DEBUG if (debug) { printf("litbits = %d\n", litbits); fflush(stdout); } #endif if (!usinghuffman) return; for (i = 1; i <= longest; i++) pfio->putbyte(num_symbols_of_length[i]); if (usinghuffman == 1) { // the literals int k = 0; for (i = 0; i <= longest; i++) { for (int j = 0; j < num_symbols_of_length[i]; j++) { if (litbits) pfio->putbits(ordered_symbols[k++],litbits); } } } fflush(stdout); maketable_encode(); } // Read the Huffman tree from the input code file. void compactor::readtree(void) { int i; int startval = 0; usinghuffman = pfio->getbit(); if (usinghuffman) { longest = pfio->lookbits(4,0xf); pfio->skipbits(4); usinghuffman += pfio->getbit(); if (usinghuffman == 2) { startval = pfio->lookbits(2,0x3); pfio->skipbits(2); } } int litbits = pfio->lookbits(4,0xf); int litmask = (1<skipbits(4); litlength = litbits; #ifdef DEBUG if (debug) { printf("\nCoder %d: readtree:\n",id); if (usinghuffman == 2) printf("Ordered Pseudo-Huffman: longest codeword = %d\n", longest); else if (usinghuffman) printf("Standard Huffman: longest codeword = %d\n", longest); else printf("Not using Huffman\n"); printf("litbits = %d\n", litbits); fflush(stdout); } #endif if (!usinghuffman) return; if (!longest) { num_symbols_of_length[0] = 1; ordered_symbols[0] = 0; } else num_symbols_of_length[0] = 0; for (i = 1; i <= longest; i++) { num_symbols_of_length[i] = pfio->lookbits(8,0xff); pfio->skipbits(8); #ifdef DEBUG if (debug) printf("num symbols of length %d = %d\n",i,num_symbols_of_length[i]); #endif } int k = 0; for (i = 0; i <= longest; i++) { for (int j = 0; j < num_symbols_of_length[i]; j++) { if (usinghuffman == 2) { ordered_symbols[k++] = startval++; } else { ordered_symbols[k++] = pfio->lookbits(litbits,litmask); pfio->skipbits(litbits); } #ifdef DEBUG if (debug) printf("%d ", ordered_symbols[k-1]); #endif } } #ifdef DEBUG if (debug) { printf("\n"); fflush(stdout); } #endif maketable_decode(); } void compactor::maketable_encode(void) { // Create coding tables // First create temporary tables ordered by code length #ifdef DEBUG // printf("(maketable_encode called by writetree:)\n", id); #endif unsigned char codelength[257]; unsigned short codebits[256]; int k = 0; for (int i = 1; i <= longest; i++) { for (int j = 0; j < num_symbols_of_length[i]; j++) codelength[k++] = i; } codelength[k] = 0; // Sentinal int lastsymbol = k; int codeword = 0; k = 0; int total = codelength[0]; while(codelength[k]) { while(codelength[k] == total) { codebits[k++] = codeword; codeword++; } codeword <<= 1; total++; } // And finally install in lookup table memset(enclength,0,256); // So all unused slots will be 0 if (!longest) { #ifdef DEBUG // printf("0-bit symbol: %d\n",ordered_symbols[0]); fflush(stdout); #endif } for (k = 0; k < lastsymbol; k++) { encode[ordered_symbols[k]] = codebits[k]; enclength[ordered_symbols[k]] = codelength[k]; #ifdef DEBUG /* printf("%d: ",ordered_symbols[k]); for (int temp = 1; temp <= codelength[k]; temp++) { if (codebits[k] & (1<<(codelength[k]-temp))) printf("1"); else printf("0"); } printf("\n"); fflush(stdout); */ #endif } } void compactor::maketable_decode(void) { // Create coding tables #ifdef DEBUG if (debug) { printf("(maketable_decode called by readtree):\n"); fflush(stdout); } #endif int tablesize = 1<putbits(symbol,litlength); #ifdef DEBUG if (debug) printf("Coder %d: uncoded symbol %d (0x%x)\n",id,symbol,symbol); #endif return; } if ((cnt = enclength[symbol]) != 0) pfio->putbits(encode[symbol],cnt); #ifdef DEBUG if (debug) printf("Coder %d: symbol %d (0x%x)\n",id,symbol,symbol); #endif if (longest && !cnt) printf("Tried to code untrained symbol %d\n",symbol); //printf("unenc = %d, coded %d bits\n",symbol,cnt); } // Member functions for the rlcomp type implement a "flagword" Huffman // coder where, if the two most frequent characters are frequent enough, the // patterns of freqchar/nextfreq/nonfreqchar are represented in a separate // coder of flag words. rlcomp::rlcomp(){ debug = 0; id = 0; inbuf = 0; procbuf = 0; symbolcnt = 0; psymbols = new compactor; pflagwords = new compactor; reset(); } rlcomp::~rlcomp() { delete [] inbuf; delete [] procbuf; delete psymbols; delete pflagwords; } int rlcomp::encodelength(int maxlength){ // Called to set up encode buffers buflength = maxlength; if (!maxlength){ // This is a decoder - no buffer inbuf = 0; procbuf = 0; return 0; } inbuf = new unsigned char [maxlength+FLAGWORDCHUNK]; procbuf = new unsigned char [(maxlength/FLAGWORDCHUNK)+1]; if (!inbuf || !procbuf) return -1; inbufend = inbuf; procbufend = procbuf; readproc = procbuf; return 1; } void rlcomp::reset() { #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif for (int i = 0; i < 256; i++) freq[i] = 0; symbolcnt = 0; doublecode = 0; inbufend = inbuf; procbufend = procbuf; readproc = procbuf; usingflagwords = 0; // Not really necessary. Set when building // or reading tree. psymbols->reset(); pflagwords->reset(); #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::buildtree(void) { #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif // Does major part of flagword coding unsigned char *readin = inbuf; unsigned char *writeproc = procbuf; unsigned char symbol; // Debug // printf("inbuf:\n"); // while(readin < inbufend) // printf("%x ", (int) *readin++); // printf("\n"); // readin = inbuf; // End debug // First find the most frequent symbol int maxfreq = -1; int i; for (i = 0; i < 256; i++) { if (freq[i] > maxfreq) { maxfreq = freq[i]; freqsymbol = i; } } int secondmaxfreq = 0; for (i = 0; i < 256; i++) { if (i == freqsymbol) continue; if (freq[i] > secondmaxfreq) { secondmaxfreq = freq[i]; secondfreqsymbol = i; } } maxfreq += secondmaxfreq; // Now see if it is frequent enough to justify using flagwords // See Cheung & Keily, "An Efficient Variable Length Coding Scheme for an // IID Source". if ((maxfreq < 16)||(maxfreq*2 < inbufend-inbuf)|| (maxfreq-secondmaxfreq == inbufend-inbuf)){ // Too few to bother or // Less than half are freqsymbol or // Single symbol #ifndef NOCOMPACTORDEBUG if (debug) printf("rlcoder %d: Not using flagwords because maxfreq = %d and numchars = %d\n",id,maxfreq,(int)(inbufend-inbuf)); #endif usingflagwords = 0; // Set up symbol coder while(readin < inbufend) psymbols->inc(*readin++); // Don't do anything with flagword coder inbufend = inbuf; // For consistency. Not necess. #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif return; } usingflagwords = 1; #ifndef NOCOMPACTORDEBUG if (debug) { printf("rlcoder %d: Using flagwords because maxfreq = %d and numchars = %d\n",id,maxfreq,(int)(inbufend-inbuf)); printf("max freq char = %d, second max freq char = %d\n", freqsymbol, secondfreqsymbol); } #endif int numproc = (int)(inbufend-inbuf+(FLAGWORDCHUNK-1))/FLAGWORDCHUNK; unsigned char *padinbufend = inbuf; padinbufend += (numproc*FLAGWORDCHUNK); for (unsigned char *p = inbufend; p < padinbufend; p++) *p = freqsymbol; int allfreq = FLAGWORDS-1; for (i = 0; i < numproc; i++) { unsigned char outbyte = 0; for (int j = 0; j < FLAGWORDCHUNK; j++) { outbyte *= 3; if ((symbol = *readin++) == freqsymbol) outbyte += 0x2; else if (symbol == secondfreqsymbol) outbyte += 0x1; else psymbols->inc(symbol); } if ((outbyte == allfreq)&&(writeproc > procbuf)&& (*(writeproc-1) >= allfreq)&& (*(writeproc-1) < MAXFLAG)) { // Check for two successive all frequent symbols // Fold into one pflagwords->dec(*(writeproc-1)); (*(writeproc-1))++; // Repeat word pflagwords->inc(*(writeproc-1)); } else { *writeproc++ = outbyte; pflagwords->inc(outbyte); } } procbufend = writeproc; // Debug // writeproc = procbuf; // printf("procbuf:\n"); // while(writeproc < procbufend) // printf("%x ", (int) *writeproc++); // printf("\n"); // End debug inbufend = inbuf; // Now to be used for debugging #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::writetree(void) { #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif buildtree(); if (usingflagwords){ putbit(1); int temp = (int) freqsymbol; int i; for (i = 7; i >= 0; i--) { putbit((temp>>i) & 1); } temp = (int) secondfreqsymbol; for (i = 7; i >= 0; i--) { putbit((temp>>i) & 1); } } else putbit(0); psymbols->writetree(); if (usingflagwords) pflagwords->writetree(); #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::readtree(void) { #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif if (getbit()) { usingflagwords = 1; int temp = 0; int i; for (i = 0; i < 8; i++) { temp += getbit(); temp <<= 1; } temp >>= 1; freqsymbol = (unsigned char) temp; temp = 0; for (i = 0; i < 8; i++) { temp += getbit(); temp <<= 1; } temp >>= 1; secondfreqsymbol = (unsigned char) temp; #ifndef NOCOMPACTORDEBUG if (debug) printf("rlcoder %d: Using flagwords, freqsymbol = %x\n",id,freqsymbol); #endif symbolcnt = 0; doublecode = 0; } else { #ifndef NOCOMPACTORDEBUG if (debug) printf("rlcoder %d: Not using flagwords\n",id); #endif usingflagwords = 0; } psymbols->readtree(); if (usingflagwords) pflagwords->readtree(); #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::maketable_encode(void){ #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif psymbols->maketable_encode(); if (usingflagwords) pflagwords->maketable_encode(); #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::maketable_decode(void) { #ifdef NOCOMPACTORDEBUG disable_compactor_debug(); #endif psymbols->maketable_decode(); if (usingflagwords) pflagwords->maketable_decode(); #ifdef NOCOMPACTORDEBUG enable_compactor_debug(); #endif } void rlcomp::incorput(int symbol, int which) { if (which) put(symbol); else inc(symbol); } void rlcomp::put(int inbyte) { if (!usingflagwords) { psymbols->put(inbyte); return; } if (inbyte != *inbufend++) { fprintf(stderr,"Internal error in rlcomp\n"); // exit(1); } if (!(symbolcnt%FLAGWORDCHUNK)) { if (doublecode) doublecode--; else { if (*readproc >= FLAGWORDS) doublecode = (*readproc+1-FLAGWORDS); pflagwords->put(*readproc++); } } if ((inbyte != freqsymbol)&&(inbyte != secondfreqsymbol)) psymbols->put(inbyte); symbolcnt++; } } // // Adaptive Prediction Trees // Image Coding // // Version 1.0 // // Based on... // // Binary tree predictive coding versions 1-5 // // John Robinson 1994, 1995, 1997, 2002, 2003, 2004 // john@userport.com // // // Helper routines for decoding, plus main decoding function // namespace apt1_0 { // #define DEBUG 1 // Following table is used to clip // Larger than 512 now, because color clipping can require more static unsigned char Aclip[3][512+256]; static unsigned char *clip[3]; const unsigned char **initclip(const int *minval, const int *maxval, int pnmtype) { int i; for (int comp = 0; comp < pnmtype; comp++) { clip[comp] = &Aclip[comp][256]; for (i = -256; i < minval[comp]; i++) clip[comp][i] = minval[comp]; for (i = minval[comp]; i < maxval[comp]; i++) clip[comp][i] = i; for (; i < 512; i++) clip[comp][i] = maxval[comp]; } return (const unsigned char **) clip; } static int Asq_decode_table[513] = { 0}; static int *sq_decode_table; static int sq_range; static int sq_out_of_range[256]; static int Adi_decode_table[513] = { 0}; static int *di_decode_table; static int di_range; static int di_out_of_range[256]; static int Aabt[513]; static int *abt; int prepare_decoder(const int& sq_quant_spacing, const int& di_quant_spacing, const int& maxval) // Sets up tables of representative levels to use in the current bands. { int i; abt = &Aabt[256]; abt[0] = 0; for (i = 1; i < 256; i++) { abt[i] = i; abt[-i] = i; } // Following four lines required to initialize tables if apt_decode() // used repeatedly in same program. Previously relied on the // static initializations above. for (i = 0; i < 513; i++) { Asq_decode_table[i] = 0; Adi_decode_table[i] = 0; } // First do the square band sq_decode_table = &Asq_decode_table[256]; int rep_level = sq_quant_spacing + (sq_quant_spacing-1)/2; if (rep_level != sq_decode_table[1]){ // Already loaded sq_range = maxval/sq_quant_spacing + 1; sq_decode_table[0] = 0; for (i = 1; i <= sq_range; i++, rep_level += sq_quant_spacing) { sq_decode_table[i] = rep_level; sq_decode_table[-i] = -rep_level; } for (i = 0; i < 256; i++) { sq_out_of_range[i] = (maxval-i)/sq_quant_spacing; } } // Then the diamond band di_decode_table = &Adi_decode_table[256]; rep_level = di_quant_spacing + (di_quant_spacing-1)/2; if (rep_level == di_decode_table[1]) // Already loaded return 0; di_range = maxval/di_quant_spacing + 1; di_decode_table[0] = 0; for (i = 1; i <= di_range; i++, rep_level += di_quant_spacing) { di_decode_table[i] = rep_level; di_decode_table[-i] = -rep_level; } for (i = 0; i < 256; i++) { di_out_of_range[i] = (maxval-i)/di_quant_spacing; } return 0; } static char commentbuf[256]; int read_header(FILE *cfp, int& pnmtype, int& rows, int& cols, int *minval, int *maxval, int& ominval, int& omaxval, int& numlevels, char& coltype, int *spacing, int *transform, char **comment, int& bilevel) { char cbuf[12]; char newline; int n_coders; /* printf("at %d\n",ftell(cfp)); fflush(stdout); cbuf[0] = fgetc(cfp); cbuf[1] = fgetc(cfp); printf("cbuf[0] = %c(%d)\n",cbuf[0],cbuf[0]); printf("cbuf[1] = %c(%d)\n",cbuf[1],cbuf[1]); fflush(stdout); fscanf(cfp,"%s %d %d%c",cbuf+2, &rows,&cols,&newline); */ fscanf(cfp,"%s %d %d%c",cbuf, &rows,&cols,&newline); pnmtype = cbuf[0] - '0';// 1 for PGM, 3 for PPM numlevels = cbuf[1] - '0'; n_coders = cbuf[2] - '0'; coltype = cbuf[3]; // 'G'=GRB, 'Y'=YUV, 'M'=monochrome, // 'B'=Bilevel monochrome, 'C'=Bilevel colour if (n_coders != numcoders%256) { // fprintf(stderr, "Sorry. This picture is coded with "); // fprintf(stderr, "%d coders. I can handle ", n_coders); // fprintf(stderr, "%d coders only\n",numcoders); return -2; } int i; char c; for (i = 0; i < 255; i++) { c = fgetc(cfp); commentbuf[i] = c; if (!c) break; } commentbuf[i] = 0; if (coltype == 'Y') fscanf(cfp,"%d %d %d %d %d %d %d %d %d%c", transform, transform+1, transform+2, transform+3, transform+4, transform+5, transform+6, transform+7, transform+8, &newline); if ((coltype == 'B')||(coltype == 'C')) bilevel = 1; else bilevel = 0; for (i = 1; i <= numlevels*2; i++) spacing[i] = fgetc(cfp); for (i = 0; i < pnmtype; i++) { minval[i] = fgetc(cfp); maxval[i] = fgetc(cfp); } ominval = fgetc(cfp); omaxval = fgetc(cfp); return 0; } void to_bilevel(unsigned char ***components, const int pnmtype, int *minval, int *maxval, const int& rows, const int& cols) { // Make the transformation for (int comp = 0; comp < pnmtype; comp++) { int thismin = minval[comp]; int thismax = maxval[comp]; for (int i = 0; i < rows; i++) { unsigned char *p = components[comp][i]; for (int j = 0; j < cols; j++) { if (*p == 0) *p = thismin; else *p = thismax; p++; } } } } // Used to have this inline, but the compiler refuses because of all the // inline code included from the codein[].get()s and codein[].getbit()s. void unmktopcode(unsigned char topcode, int& l0rec, int& l1rec, int& l2rec, int& r0rec, int& r1rec, int& r2rec, int& cdcoder, int& efcoder, rlcomp *codein, const int neg_correct, int pnmtype) { int tc = (int) topcode; if (pnmtype == 1) { if (tc == 52){ if (codein[0].getbit()) // Special case for monochrome // handling of bilevel images // See encode.cpp tc = 49; } if (tc == 56){ if (codein[0].getbit()) // Special case for monochrome // handling of bilevel images // See encode.cpp tc = 50; } if (tc & 0x20) cdcoder = 1; if (tc & 0x10) efcoder = 1; if (tc & 0xc){ // Just going to test for non-zero switch(tc & 0xc) { case 0x4: l0rec = 1; break; case 0x8: l0rec = 2; break; case 0xc: l0rec = codein[0].get(); break; } } if (tc & 0x3){ switch(tc & 0x3) { case 0x1: r0rec = 1; break; case 0x2: r0rec = 2; break; case 0x3: r0rec = codein[0].get(); break; } } } else { if (tc & 0x1) cdcoder = 1; if (tc & 0x2) efcoder = 1; if (tc & 0x4){ l0rec = codein[0].get(); } if (tc & 0x10){ l1rec = codein[1].get(); } if (tc & 0x40){ l2rec = codein[2].get(); } if (tc & 0x8){ r0rec = codein[0].get(); } if (tc & 0x20){ r1rec = codein[1].get(); } if (tc & 0x80){ r2rec = codein[2].get(); } } } // // CLIP version of dec_level generated by running // g++ -E encode.cpp on original APT version, then // substituting output of that into this file. // Thereby remove dependence on macro.cpp // void dec_level(unsigned char ***out, const int& XMAX, const int& YMAX, const int& level, const int& sq_spacing, const int& di_spacing, rlcomp *codein, const int pnmtype, const int& maxval, int actuallower[3][256], int actualhigher[3][256]) { unsigned char *myclip; for (int ii = 0; ii < numcoders; ii++) codein[ii].readtree(); prepare_decoder(sq_spacing, di_spacing, maxval); int error; int X, Y; int p0, p1, p2, q0, q1, q2, r0, r1, r2, s0, s1, s2; int t0, t1, t2, u0, u1, u2, v0, v1, v2; int comp; int estimate; int tempout; int sq_ridgethresh = get_ridgethresh(sq_spacing); if (maxval < sq_ridgethresh) sq_ridgethresh = maxval; int di_ridgethresh = get_ridgethresh(di_spacing); if (maxval < di_ridgethresh) di_ridgethresh = maxval; const int XINTERVAL = 1 << level; const int YINTERVAL = 1 << level; const int XHINT = XINTERVAL >> 1; const int YHINT = YINTERVAL >> 1; const int XQINT = XHINT >> 1; const int YQINT = YHINT >> 1; for (Y = YHINT; Y < YMAX; Y += YINTERVAL) { int yplus = Y+YHINT; int yqplus = Y+YQINT; int yqminus = Y-YQINT; int yminus = Y-YHINT; int y2minus = Y-YINTERVAL; if (y2minus < 0) y2minus = 0; unsigned char *pyplus0, *pyplus1, *pyplus2; unsigned char *pyqplus0, *pyqplus1, *pyqplus2; unsigned char *pyqminus0, *pyqminus1, *pyqminus2; unsigned char *pyminus0, *pyminus1, *pyminus2; unsigned char *py2minus0, *py2minus1, *py2minus2; unsigned char *py0, *py1, *py2; if (yplus == YMAX) { pyplus0 = out[0][yminus]; pyplus1 = out[1][yminus]; pyplus2 = out[2][yminus]; } else { pyplus0 = out[0][yplus]; pyplus1 = out[1][yplus]; pyplus2 = out[2][yplus]; } pyqplus0 = out[0][yqplus]; pyqminus0 = out[0][yqminus]; pyminus0 = out[0][yminus]; py2minus0 = out[0][y2minus]; py0 = out[0][Y]; pyqplus1 = out[1][yqplus]; pyqminus1 = out[1][yqminus]; pyminus1 = out[1][yminus]; py2minus1 = out[1][y2minus]; py1 = out[1][Y]; pyqplus2 = out[2][yqplus]; pyqminus2 = out[2][yqminus]; pyminus2 = out[2][yminus]; py2minus2 = out[2][y2minus]; py2 = out[2][Y]; v0 = q0 = pyminus0[0]; r0 = pyplus0[0]; v1 = q1 = pyminus1[0]; r1 = pyplus1[0]; v2 = q2 = pyminus2[0]; r2 = pyplus2[0]; unsigned char topcode; int leftsibling = 1; int xplus = XINTERVAL; int xminus = 0; for (X = XHINT; X < XMAX; X+=XINTERVAL, xplus+=XINTERVAL, xminus+=XINTERVAL) { p0 = q0; s0 = r0; u0 = v0; p1 = q1; s1 = r1; u1 = v1; p2 = q2; s2 = r2; u2 = v2; if (xplus != XMAX) { q0 = pyminus0[xplus]; r0 = pyplus0[xplus]; q1 = pyminus1[xplus]; r1 = pyplus1[xplus]; q2 = pyminus2[xplus]; r2 = pyplus2[xplus]; } register int predictor; int cdcoder; int efcoder; int cpropagate, dpropagate; int a0rec = 0; int a1rec = 0; int a2rec = 0; int b0rec; int b1rec; int b2rec; int c0rec = 0; int c1rec = 0; int c2rec = 0; int d0rec = 0; int d1rec = 0; int d2rec = 0; topcode = 0; if (leftsibling) { b0rec = 0; b1rec = 0; b2rec = 0; cdcoder = efcoder = 0; if (py0[X]) { topcode = codein[SST+3].get(); unmktopcode(topcode, a0rec, a1rec, a2rec, b0rec, b1rec, b2rec, cdcoder,efcoder,codein+SST,sq_range, pnmtype); } leftsibling = 0; } else { a0rec = b0rec; a1rec = b1rec; a2rec = b2rec; cdcoder = efcoder; leftsibling = 1; } error = a0rec; comp = 0; myclip = clip[0]; predictor = 255; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((q0))-((p0)))) > 0) { order = 2*27; biggest = ((q0)); smallest = ((p0)); } else { if (temp == 0) order = 27; biggest = ((p0)); smallest = ((q0)); } if ((temp=(((r0))-((q0)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((s0))-((r0)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((p0))-((s0)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((r0)) > biggest) biggest = ((r0)); else if (((r0)) < smallest) smallest = ((r0)); if (((s0)) > biggest) biggest = ((s0)); else if (((s0)) < smallest) smallest = ((s0)); if ((biggest - smallest < (sq_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((p0))+((q0))+((r0))+((s0))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((q0))+((s0)))>>1; else if (predictor == 2) estimate = (((p0))+((r0)))>>1; else if (predictor == 3) estimate = (((Y!=YHINT)&&(xplus!=XMAX))) ? ((py2minus0)[X]) : (((p0))+((q0)))>>1; else estimate = (((X!=XHINT)&&(yplus!=YMAX))) ? ((py0)[X-XINTERVAL]): (((p0))+((s0)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p0)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((q0))+((s0)))>>1; predictor = 1; } else { estimate = (((p0))+((r0)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((q0))+((r0)))>>1; else if (predictor == 8) estimate = (((s0))+((r0)))>>1; else if (predictor == 9) estimate = (((p0))+((s0)))>>1; else estimate = (((q0))+((p0)))>>1; } else { if (predictor == 11) estimate = ((p0)); else if (predictor == 12) estimate = ((r0)); else if (predictor == 13) estimate = ((q0)); else estimate = ((s0)); } if (!error) { ((v0)) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (sq_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (sq_range) - error; if (error>(sq_out_of_range)[estimate]) error = (sq_decode_table)[error-(sq_range)]; else error = (sq_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; ((v0)) = myclip[error+estimate]; } (py0)[X] = ((v0)); error = abt[((v0)) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-((v0))]; if ((temp=abt[((v0)) - ((((q0))+((s0)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[((v0)) - ((((p0))+((r0)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[((v0)) - ((((q0))+((r0)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[((v0)) - ((((s0))+((r0)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[((v0)) - ((((p0))+((s0)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[((v0)) - ((((p0))+((q0)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((p0))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((r0))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((q0))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((s0))]) < error) { error = temp; predictor = 14; } if (((Y!=YHINT)&&(xplus!=XMAX)) && ((temp=offabt[((py2minus0)[X])]) < error)) { error = temp; predictor = 3; } if (((X!=XHINT)&&(yplus!=YMAX)) && ((temp=offabt[((py0)[X-XINTERVAL])]) < error)) { error = temp; predictor = 4; } } if (pnmtype > 1) { error = a1rec; comp = 1; myclip = clip[1]; if (!predictor) estimate = (((p1))+((q1))+((r1))+((s1))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((q1))+((s1)))>>1; else if (predictor == 2) estimate = (((p1))+((r1)))>>1; else if (predictor == 3) estimate = (((Y!=YHINT)&&(xplus!=XMAX))) ? ((py2minus1)[X]) : (((p1))+((q1)))>>1; else estimate = (((X!=XHINT)&&(yplus!=YMAX))) ? ((py1)[X-XINTERVAL]): (((p1))+((s1)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p1)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((q1))+((s1)))>>1; predictor = 1; } else { estimate = (((p1))+((r1)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((q1))+((r1)))>>1; else if (predictor == 8) estimate = (((s1))+((r1)))>>1; else if (predictor == 9) estimate = (((p1))+((s1)))>>1; else estimate = (((q1))+((p1)))>>1; } else { if (predictor == 11) estimate = ((p1)); else if (predictor == 12) estimate = ((r1)); else if (predictor == 13) estimate = ((q1)); else estimate = ((s1)); } if (!error) { ((v1)) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (sq_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (sq_range) - error; if (error>(sq_out_of_range)[estimate]) error = (sq_decode_table)[error-(sq_range)]; else error = (sq_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; ((v1)) = myclip[error+estimate]; } (py1)[X] = ((v1)); error = a2rec; comp = 2; myclip = clip[2]; if (!predictor) estimate = (((p2))+((q2))+((r2))+((s2))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((q2))+((s2)))>>1; else if (predictor == 2) estimate = (((p2))+((r2)))>>1; else if (predictor == 3) estimate = (((Y!=YHINT)&&(xplus!=XMAX))) ? ((py2minus2)[X]) : (((p2))+((q2)))>>1; else estimate = (((X!=XHINT)&&(yplus!=YMAX))) ? ((py2)[X-XINTERVAL]): (((p2))+((s2)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p2)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((q2))+((s2)))>>1; predictor = 1; } else { estimate = (((p2))+((r2)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((q2))+((r2)))>>1; else if (predictor == 8) estimate = (((s2))+((r2)))>>1; else if (predictor == 9) estimate = (((p2))+((s2)))>>1; else estimate = (((q2))+((p2)))>>1; } else { if (predictor == 11) estimate = ((p2)); else if (predictor == 12) estimate = ((r2)); else if (predictor == 13) estimate = ((q2)); else estimate = ((s2)); } if (!error) { ((v2)) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (sq_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (sq_range) - error; if (error>(sq_out_of_range)[estimate]) error = (sq_decode_table)[error-(sq_range)]; else error = (sq_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; ((v2)) = myclip[error+estimate]; } (py2)[X] = ((v2)); } topcode = 0; cpropagate = dpropagate = 0; if (cdcoder) { topcode = codein[DST+3].get(); unmktopcode(topcode, c0rec, c1rec, c2rec, d0rec, d1rec, d2rec, cpropagate, dpropagate,codein+DST,di_range, pnmtype); } if (!yminus) { t0 = p0; t1 = p1; t2 = p2; } else { t0 = py2minus0[X]; t1 = py2minus1[X]; t2 = py2minus2[X]; } error = c0rec; comp = 0; myclip = clip[0]; predictor = 255; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((t0))-((p0)))) > 0) { order = 2*27; biggest = ((t0)); smallest = ((p0)); } else { if (temp == 0) order = 27; biggest = ((p0)); smallest = ((t0)); } if ((temp=(((q0))-((t0)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((v0))-((q0)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((p0))-((v0)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((q0)) > biggest) biggest = ((q0)); else if (((q0)) < smallest) smallest = ((q0)); if (((v0)) > biggest) biggest = ((v0)); else if (((v0)) < smallest) smallest = ((v0)); if ((biggest - smallest < (di_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((p0))+((t0))+((q0))+((v0))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((t0))+((v0)))>>1; else if (predictor == 2) estimate = (((p0))+((q0)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((py2minus0)[xminus]) : (((p0))+((t0)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((py2minus0)[xplus]): (((p0))+((v0)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p0)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((t0))+((v0)))>>1; predictor = 1; } else { estimate = (((p0))+((q0)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((t0))+((q0)))>>1; else if (predictor == 8) estimate = (((v0))+((q0)))>>1; else if (predictor == 9) estimate = (((p0))+((v0)))>>1; else estimate = (((t0))+((p0)))>>1; } else { if (predictor == 11) estimate = ((p0)); else if (predictor == 12) estimate = ((q0)); else if (predictor == 13) estimate = ((t0)); else estimate = ((v0)); } if (level > 1) pyqminus0[X-XQINT] = cpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (pyminus0)[X] = (tempout); error = abt[(tempout) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-(tempout)]; if ((temp=abt[(tempout) - ((((t0))+((v0)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[(tempout) - ((((p0))+((q0)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[(tempout) - ((((t0))+((q0)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[(tempout) - ((((v0))+((q0)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[(tempout) - ((((p0))+((v0)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[(tempout) - ((((p0))+((t0)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((p0))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((q0))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((t0))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((v0))]) < error) { error = temp; predictor = 14; } if (((xminus) &&(yminus)) && ((temp=offabt[((py2minus0)[xminus])]) < error)) { error = temp; predictor = 3; } if (((xplus!=XMAX)&&(yminus)) && ((temp=offabt[((py2minus0)[xplus])]) < error)) { error = temp; predictor = 4; } } if (pnmtype > 1) { error = c1rec; comp = 1; myclip = clip[1]; if (!predictor) estimate = (((p1))+((t1))+((q1))+((v1))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((t1))+((v1)))>>1; else if (predictor == 2) estimate = (((p1))+((q1)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((py2minus1)[xminus]) : (((p1))+((t1)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((py2minus1)[xplus]): (((p1))+((v1)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p1)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((t1))+((v1)))>>1; predictor = 1; } else { estimate = (((p1))+((q1)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((t1))+((q1)))>>1; else if (predictor == 8) estimate = (((v1))+((q1)))>>1; else if (predictor == 9) estimate = (((p1))+((v1)))>>1; else estimate = (((t1))+((p1)))>>1; } else { if (predictor == 11) estimate = ((p1)); else if (predictor == 12) estimate = ((q1)); else if (predictor == 13) estimate = ((t1)); else estimate = ((v1)); } if (level > 1) pyqminus0[X-XQINT] = cpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (pyminus1)[X] = (tempout); error = c2rec; comp = 2; myclip = clip[2]; if (!predictor) estimate = (((p2))+((t2))+((q2))+((v2))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((t2))+((v2)))>>1; else if (predictor == 2) estimate = (((p2))+((q2)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((py2minus2)[xminus]) : (((p2))+((t2)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((py2minus2)[xplus]): (((p2))+((v2)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p2)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((t2))+((v2)))>>1; predictor = 1; } else { estimate = (((p2))+((q2)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((t2))+((q2)))>>1; else if (predictor == 8) estimate = (((v2))+((q2)))>>1; else if (predictor == 9) estimate = (((p2))+((v2)))>>1; else estimate = (((t2))+((p2)))>>1; } else { if (predictor == 11) estimate = ((p2)); else if (predictor == 12) estimate = ((q2)); else if (predictor == 13) estimate = ((t2)); else estimate = ((v2)); } if (level > 1) pyqminus0[X-XQINT] = cpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (pyminus2)[X] = (tempout); } error = d0rec; comp = 0; myclip = clip[0]; predictor = 255; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((p0))-((u0)))) > 0) { order = 2*27; biggest = ((p0)); smallest = ((u0)); } else { if (temp == 0) order = 27; biggest = ((u0)); smallest = ((p0)); } if ((temp=(((v0))-((p0)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((s0))-((v0)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((u0))-((s0)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((v0)) > biggest) biggest = ((v0)); else if (((v0)) < smallest) smallest = ((v0)); if (((s0)) > biggest) biggest = ((s0)); else if (((s0)) < smallest) smallest = ((s0)); if ((biggest - smallest < (di_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((u0))+((p0))+((v0))+((s0))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((p0))+((s0)))>>1; else if (predictor == 2) estimate = (((u0))+((v0)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((pyminus0)[X-XINTERVAL]) : (((u0))+((p0)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((pyminus0)[X]): (((u0))+((s0)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((u0)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((p0))+((s0)))>>1; predictor = 1; } else { estimate = (((u0))+((v0)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((p0))+((v0)))>>1; else if (predictor == 8) estimate = (((s0))+((v0)))>>1; else if (predictor == 9) estimate = (((u0))+((s0)))>>1; else estimate = (((p0))+((u0)))>>1; } else { if (predictor == 11) estimate = ((u0)); else if (predictor == 12) estimate = ((v0)); else if (predictor == 13) estimate = ((p0)); else estimate = ((s0)); } if (level > 1) pyqplus0[X-XQINT] = dpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (py0)[xminus] = (tempout); error = abt[(tempout) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-(tempout)]; if ((temp=abt[(tempout) - ((((p0))+((s0)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[(tempout) - ((((u0))+((v0)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[(tempout) - ((((p0))+((v0)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[(tempout) - ((((s0))+((v0)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[(tempout) - ((((u0))+((s0)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[(tempout) - ((((u0))+((p0)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((u0))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((v0))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((p0))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((s0))]) < error) { error = temp; predictor = 14; } if (((xminus) &&(yminus)) && ((temp=offabt[((pyminus0)[X-XINTERVAL])]) < error)) { error = temp; predictor = 3; } if (((xplus!=XMAX)&&(yminus)) && ((temp=offabt[((pyminus0)[X])]) < error)) { error = temp; predictor = 4; } } if (pnmtype > 1) { error = d1rec; comp = 1; myclip = clip[1]; if (!predictor) estimate = (((u1))+((p1))+((v1))+((s1))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((p1))+((s1)))>>1; else if (predictor == 2) estimate = (((u1))+((v1)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((pyminus1)[X-XINTERVAL]) : (((u1))+((p1)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((pyminus1)[X]): (((u1))+((s1)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((u1)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((p1))+((s1)))>>1; predictor = 1; } else { estimate = (((u1))+((v1)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((p1))+((v1)))>>1; else if (predictor == 8) estimate = (((s1))+((v1)))>>1; else if (predictor == 9) estimate = (((u1))+((s1)))>>1; else estimate = (((p1))+((u1)))>>1; } else { if (predictor == 11) estimate = ((u1)); else if (predictor == 12) estimate = ((v1)); else if (predictor == 13) estimate = ((p1)); else estimate = ((s1)); } if (level > 1) pyqplus0[X-XQINT] = dpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (py1)[xminus] = (tempout); error = d2rec; comp = 2; myclip = clip[2]; if (!predictor) estimate = (((u2))+((p2))+((v2))+((s2))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((p2))+((s2)))>>1; else if (predictor == 2) estimate = (((u2))+((v2)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((pyminus2)[X-XINTERVAL]) : (((u2))+((p2)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((pyminus2)[X]): (((u2))+((s2)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((u2)); } else if (predictor == 6) { if (codein[0].getbit()) { estimate = (((p2))+((s2)))>>1; predictor = 1; } else { estimate = (((u2))+((v2)))>>1; predictor = 2; } } else if (predictor == 7) estimate = (((p2))+((v2)))>>1; else if (predictor == 8) estimate = (((s2))+((v2)))>>1; else if (predictor == 9) estimate = (((u2))+((s2)))>>1; else estimate = (((p2))+((u2)))>>1; } else { if (predictor == 11) estimate = ((u2)); else if (predictor == 12) estimate = ((v2)); else if (predictor == 13) estimate = ((p2)); else estimate = ((s2)); } if (level > 1) pyqplus0[X-XQINT] = dpropagate; if (!error) { (tempout) = estimate; } else { if (error & 1) error = (error+1)>>1; else { error = error>>1; error = (di_range) - error; } if ((actuallower[comp][estimate] > actualhigher[comp][estimate])&&error) error = (di_range) - error; if (error>(di_out_of_range)[estimate]) error = (di_decode_table)[error-(di_range)]; else error = (di_decode_table)[error]; if (error > 0) actualhigher[comp][estimate]++; else if (error < 0) actuallower[comp][estimate]++; (tempout) = myclip[error+estimate]; } (py2)[xminus] = (tempout); } } } } void dec_plane(unsigned char ***out,const int& xmax, const int& ymax, const int& fromlevel, const int *spacing, rlcomp *codein, const int& minval, const int& maxval, const int pnmtype) { // printf("decplane\n"); fflush(stdout); int actualhigher[3][256]; int actuallower[3][256]; for (int act = 0; act < 256; act++){ actualhigher[0][act] = actuallower[0][act] = 0; actualhigher[1][act] = actuallower[1][act] = 0; actualhigher[2][act] = actuallower[2][act] = 0; } int X, Y; int predpel; // Fill the first level pels with 1's to indicate that these // errors must be fetched (not below the leaf of a tree int inc = 1 << fromlevel; for (Y = inc/2; Y < ymax; Y += inc) for (X = inc/2; X < xmax; X += inc) { out[0][Y][X] = 1; } // Get dpcm lowpass for (int comp = 0; comp < pnmtype; comp++) { codein[0].readtree(); for (Y = 0; Y < ymax; Y += inc) for (X = 0; X < xmax; X += inc) { predpel = (X ? out[comp][Y][X-inc] : (Y ? out[comp][Y-inc][X] : 0)); int temp = codein[0].get() + predpel; if (temp < 0) temp += 256; if (temp > 255) temp -= 256; out[comp][Y][X] = temp; } } int tmaxval[3], tminval[3]; tmaxval[0] = tmaxval[1] = tmaxval[2] = maxval; tminval[0] = tminval[1] = tminval[2] = minval; initclip(tminval, tmaxval,pnmtype); int band = 1; for (int level = fromlevel; level > 0; level--, band += 2) { dec_level(out,xmax,ymax,level,spacing[band], spacing[band+1], codein, pnmtype, maxval, actuallower, actualhigher); } /* for (int act = minval; act <= maxval; act++) printf("%d: lower = %d, higher = %d\n", act, actuallower[0][act], actualhigher[0][act]); for (int act = minval; act <= maxval; act++) printf("%d: lower = %d, higher = %d\n", act, actuallower[1][act], actualhigher[1][act]); for (int act = minval; act <= maxval; act++) printf("%d: lower = %d, higher = %d\n", act, actuallower[2][act], actualhigher[2][act]); */ } static int numlevels; int eight_bit_decode(unsigned char ***components, FILE *cfp, int& rows, int& cols, int& pnmtype, int& topmaxval, char **comment, int if_eq_two_alloc_double_space, cfileio& fio, rlcomp *codein) { // Last argument allows set up of correctsize row buffers which // will later be used for converting this and another 8-bit // decode into a 16-bit decode. int minval[3], maxval[3];// Min and max values of original picture int ominval, omaxval; // Overall min and max of transformed picture char coltype; int spacing[65]; int i; int compnum; int transform[9]; int bilevel; if ((i = read_header(cfp, pnmtype, rows, cols, minval, maxval, ominval, omaxval, numlevels, coltype, spacing, transform, comment, bilevel))) { // fclose(cfp); return i; } *comment = commentbuf; // Make XMAX and YMAX equal to next highest multiple of 16 int YMAX = ((rows+(1<>numlevels)<>(numlevels+1))<<(numlevels+1); // Set up decoders // Allocate space for decoded picture // If decoding into an existing array (e.g. for immediate display) // create line pointers out[i], and point at rows of array. unsigned char **out; for (compnum = 0; compnum < pnmtype; compnum++) { components[compnum] = out = new unsigned char*[YMAX]; for (i = 0; i < YMAX; i++) { out[i] = new unsigned char[XMAX*if_eq_two_alloc_double_space]; memset(out[i],0,XMAX*if_eq_two_alloc_double_space); } } if (pnmtype == 1) { components[2] = components[1] = components[0]; } // Decode all components (CHANGE NAME OF dec_plane LATER dec_plane(components,XMAX,YMAX,numlevels, spacing, codein, ominval, omaxval, pnmtype); // Convert colourspace back if necessary if (coltype == 'Y') { const unsigned char **clip = initclip(minval,maxval,pnmtype); // ycc_rgb(components,rows,cols,clip,'0'); pca_rgb(components,transform,rows,cols,clip); } if ((coltype == 'B')||(coltype == 'C')) to_bilevel(components, pnmtype, minval, maxval, rows, cols); topmaxval = maxval[0]; if (pnmtype == 3) { if (maxval[1] > topmaxval) topmaxval = maxval[1]; if (maxval[2] > topmaxval) topmaxval = maxval[2]; } fio.rewind(); // Unload any pre-read characters return 1; } int apt_decode(unsigned char ***components, FILE *cfp, int& rows, int& cols, int& pnmtype, int& topmaxval, char **comment, int& bytesperpel) { cfileio fio(cfp); rlcomp codein[numcoders]; for (int i = 0; i < numcoders; i++) { // codein[i].init(); codein[i].attach(&fio); codein[i].indexnumber(i); // For debugging #ifdef DEBUG codein[i].debugon(); #endif } int numbits; int quality; char cbuf[12]; fread(cbuf,1,11,cfp); quality = cbuf[9]; numbits = cbuf[8] - '0'; cbuf[6] = 0; // So following string operations work ok // (Ignore encoder subversion and initial pnmtype in decoder) int retval = 0; // printf("numbits = %d\n",numbits); if (strcmp(cbuf,"apt1.0")) { // fprintf(stderr, "This is not a valid apt 1.0 file\n"); return -2; } if (cbuf[10] == 'P') { palettize pal(cfp,fio); // Reads palette bytesperpel = 1; retval = eight_bit_decode(components, cfp, rows, cols, pnmtype, topmaxval, comment, 1, fio, codein); pal.frompalette(components, rows, cols); } else if (cbuf[10] == 'p') { bwpalettize pal(cfp,fio); // Reads palette bytesperpel = 1; retval = eight_bit_decode(components, cfp, rows, cols, pnmtype, topmaxval, comment, 1, fio, codein); pal.frompalette(components, rows, cols); } else if (numbits <= 8) { bytesperpel = 1; retval = eight_bit_decode(components, cfp, rows, cols, pnmtype, topmaxval, comment, 1, fio, codein); } else { bytesperpel = 2; unsigned char **othercomponents[3]; char backupcomment[256]; if ((retval = eight_bit_decode(components, cfp, rows, cols, pnmtype, topmaxval, comment, 2, fio, codein)) < 0) return retval; topmaxval = 0; // Got to replace with real one int pelval; if (quality == 100) { // Lower bits coded too strcpy(backupcomment,commentbuf); retval = eight_bit_decode(othercomponents, cfp, rows, cols, pnmtype, topmaxval, comment, 1, fio, codein); // Interleave bits in rows for (int compnum = 0; compnum < pnmtype; compnum++) { for (int i = 0; i < rows; i++) { unsigned char *outp = components[compnum][i]; unsigned char *ap = components[compnum][i]; unsigned char *bp = othercomponents[compnum][i]; // Go from end of line to start so don't // overwrite stuff outp += 2*(cols-1); ap += cols-1; bp += cols-1; for (int j = 0; j < cols; j++) { pelval = ((int)*ap--) << (numbits-8); pelval |= *bp--; if (pelval > topmaxval) topmaxval = pelval; *outp = (pelval>>8); *(outp+1) = pelval & 0xff; outp -= 2; } } } strcpy(commentbuf,backupcomment); apt_finish(othercomponents, rows, cols, pnmtype); } else { // Lower bits not coded. Still have to convert to 16bit for (int compnum = 0; compnum < pnmtype; compnum++) { for (int i = 0; i < rows; i++) { unsigned char *outp = components[compnum][i]; unsigned char *ap = components[compnum][i]; // Go from end of line to start so don't // overwrite stuff outp += 2*(cols-1); ap += cols-1; for (int j = 0; j < cols; j++) { pelval = ((int)*ap--) << (numbits-8); pelval |= 1<<(numbits-9); if (pelval > topmaxval) topmaxval = pelval; *outp = (pelval>>8); *(outp+1) = pelval & 0xff; outp -= 2; } } } } } // fclose(cfp); return retval; } void apt_finish(unsigned char ***components, int rows, int cols, int pnmtype) { int YMAX = ((rows+(1<>numlevels)<>enc_numlevels)<>(enc_numlevels+1))<<(enc_numlevels+1); if (pnmtype == 1) // PGM { unsigned char **buf; *pbuf++ = buf = new unsigned char *[ymax]; *pbuf++ = buf; *pbuf = buf; // So all three components point at same for (i = 0; i < rows; i++) { buf[i] = new unsigned char[xmax]; memcpy(buf[i],incomponents[0][i],cols); for (j = cols; j < xmax; j++) // Pad image by buf[i][j] = buf[i][cols-1]; // copying last pel } // Vertical padding for (i = rows; i < ymax; i++) { buf[i] = new unsigned char[xmax]; // Pad image by memcpy(buf[i], buf[rows-1], xmax); // copying last line } } else // PPM { unsigned char **bufr, **bufg, **bufb; *pbuf++ = bufg = new unsigned char *[ymax]; *pbuf++ = bufr = new unsigned char *[ymax]; *pbuf = bufb = new unsigned char *[ymax]; for (i = 0; i < rows; i++){ bufg[i] = new unsigned char[xmax]; bufr[i] = new unsigned char[xmax]; bufb[i] = new unsigned char[xmax]; memcpy(bufg[i],incomponents[0][i],cols); memcpy(bufr[i],incomponents[1][i],cols); memcpy(bufb[i],incomponents[2][i],cols); for (j = cols; j < xmax; j++) { // Pad bufr[i][j] = bufr[i][cols-1]; bufg[i][j] = bufg[i][cols-1]; bufb[i][j] = bufb[i][cols-1]; } } // Vertical padding for (i = rows; i < ymax; i++) { bufg[i] = new unsigned char[xmax]; bufr[i] = new unsigned char[xmax]; bufb[i] = new unsigned char[xmax]; memcpy(bufg[i], bufg[rows-1], xmax); // copying last line memcpy(bufr[i], bufr[rows-1], xmax); // copying last line memcpy(bufb[i], bufb[rows-1], xmax); // copying last line } } } static int Asq_encode_table[513] = { 0}; static int *sq_encode_table; static int sq_arr[19]; // Large enough for any number of levels we might use static int Adi_encode_table[513] = { 0}; static int *di_encode_table; static int di_arr[19]; // Large enough for any number of levels we might use void prepare_encoder(const int& sq_quant_spacing, const int& di_quant_spacing, const int& maximum) // Sets up tables of representative levels to use in the current bands. { int i; abt = &Aabt[256]; abt[0] = 0; for (i = 1; i < 256; i++) { abt[i] = i; abt[-i] = i; } // Following six lines required to initialize tables if apt_encode() // used repeatedly in same program. Previously relied on the // static initializations above. for (i = 0; i < 513; i++) { Asq_encode_table[i] = 0; Adi_encode_table[i] = 0; Asq_decode_table[i] = 0; Adi_decode_table[i] = 0; } // First do the square band // printf("Square band quantization: spacing = %d\n",sq_quant_spacing); sq_range = maximum/sq_quant_spacing + 1; sq_encode_table = &Asq_encode_table[256]; for (i = 1; i < 256; i++) { sq_encode_table[i] = i/sq_quant_spacing; sq_encode_table[-i] = -i/sq_quant_spacing; } sq_decode_table = &Asq_decode_table[256]; int rep_level = sq_quant_spacing + (sq_quant_spacing-1)/2; // printf("Range = %d, first rep level = %d.\n",sq_range,rep_level); sq_decode_table[0] = 0; for (i = 1; i <= sq_range; i++, rep_level += sq_quant_spacing) { sq_decode_table[i] = rep_level; sq_decode_table[-i] = -rep_level; } // Then the diamond band // printf("Diamond band quantization: spacing = %d\n",di_quant_spacing); di_range = maximum/di_quant_spacing + 1; di_encode_table = &Adi_encode_table[256]; for (i = 1; i < 256; i++) { di_encode_table[i] = i/di_quant_spacing; di_encode_table[-i] = -i/di_quant_spacing; } di_decode_table = &Adi_decode_table[256]; rep_level = di_quant_spacing + (di_quant_spacing-1)/2; // printf("Range = %d, first rep level = %d.\n",di_range,rep_level); di_decode_table[0] = 0; for (i = 1; i <= di_range; i++, rep_level += di_quant_spacing) { di_decode_table[i] = rep_level; di_decode_table[-i] = -rep_level; } } static unsigned char **scratch; static unsigned char **predictors; static unsigned char **ridge; #ifdef DEBUG static unsigned char **debugimage; #endif int makescratch(const int& xmax, const int& ymax) // Make space for scratch images. Assume we don't run out of space. { scratch = new unsigned char *[ymax]; ridge = new unsigned char *[ymax]; #ifdef DEBUG debugimage = new unsigned char *[ymax]; #endif predictors = new unsigned char *[ymax]; for (int i = 0; i < ymax; i++) { scratch[i] = new unsigned char[xmax]; ridge[i] = new unsigned char[xmax]; memset(ridge[i], '\0', xmax); #ifdef DEBUG debugimage[i] = new unsigned char[xmax]; memset(debugimage[i], '\0', xmax); #endif predictors[i] = new unsigned char[xmax]; memset(predictors[i], 255, xmax); } return 0; } int deletescratch(const int& ymax) { for (int i = 0; i < ymax; i++) { delete [] scratch[i]; delete [] ridge[i]; #ifdef DEBUG delete [] debugimage[i]; #endif delete [] predictors[i]; } delete [] scratch; delete [] ridge; #ifdef DEBUG delete [] debugimage; #endif delete [] predictors; return 0; } // Calculate quantization levels from quality parameter int calcq(int dynamicrange, int quality, int *qvals, int numbands) { if ((quality > 100) || (quality < 0)) return -1; int topval = (100-quality)/2; // topval will be the quantizer spacing on the finest band // topval *= dynamicrange; // topval /= 255; if (!topval) { // Quality == 100 or scaling makes it so for (int i = 0; i < numbands; i++) *qvals++ = 1; return 1; } double qratio; if (topval >= 40) { topval += (topval-25) + 3*(topval-40); // qratio = 0.75; qratio = 0.80; } else if (topval > 25) { topval += (topval-25); // qratio = 0.75; qratio = 0.80; } else qratio = 0.8; // qratio = 0.85; qvals[numbands-1] = topval; int i; for (i = numbands-2; i >= 0; i--) // qvals[i] = (int)((((double)qvals[i+1])*qratio)+0.99999); qvals[i] = (int)((((double)qvals[i+1])*qratio)+0.6666); #ifdef DEBUG printf("Quality parameter = %d, dynamic range = %d.\n", quality, dynamicrange); printf("Quantizater spacings: "); for (i = 0; i < numbands; i++) { printf("%d ",qvals[i]); } printf("\n"); #endif return 1; } void gettrueminmax(unsigned char ***components, const int pnmtype, int *minval, int *maxval, const int& rows, const int& cols){ unsigned char *p; for (int comp = 0; comp < pnmtype; comp++) { int minv = 256; int maxv = -1; for (int i = 0; i < rows; i++) { p = components[comp][i]; for (int j = 0; j < cols; j++) { if (*p < minv) minv = *p; if (*p > maxv) maxv = *p; p++; } } *minval++ = minv; *maxval++ = maxv; } } void from_bilevel(unsigned char ***components, const int pnmtype, int *minval, int *maxval, const int& rows, const int& cols, int &bilevel) { // do work to check if only two values bilevel = 0; unsigned char *p; int firstval = minval[0]; int secondval = -1; int comp; for (comp = 0; comp < pnmtype; comp++) { for (int i = 0; i < rows; i++) { p = components[comp][i]; for (int j = 0; j < cols; j++) { if (secondval < 0) { if (*p != firstval) secondval = *p; } else { if ((*p != firstval)&& (*p != secondval)) { return; } } p++; } } } bilevel = 1; // Got to here so it is bilevel if (secondval < firstval){ // Conceivable if first comp is flat and // second is that value and one lower firstval = secondval; secondval = minval[0]; } // Make the transformation for (comp = 0; comp < pnmtype; comp++) { for (int i = 0; i < rows; i++) { p = components[comp][i]; for (int j = 0; j < cols; j++) { if (*p == firstval) *p = 0; else *p = 1; p++; } } } } // // CLIP version of enc_level generated by running // g++ -E encode.cpp on original APT version, then // substituting output of that into this file. // Thereby remove dependence on macro.cpp // void enc_level(unsigned char **in, const int& XMAX, const int& YMAX, const int& level, const int& sq_spacing, const int& di_spacing, const int& minval, const int& maxval, const int& comp, const int& truecols, const int& truerows, int actuallower[], int actualhigher[]) { register int error; int code; register int prepel; int X, Y; int ridgepel; register int p,q,r,s, t, u, v; register int estimate; register int elow, ehigh; prepare_encoder(sq_spacing, di_spacing, maxval); sq_arr[level] = sq_range; di_arr[level] = di_range; int sq_ridgethresh = get_ridgethresh(sq_spacing); if (maxval < sq_ridgethresh) sq_ridgethresh = maxval; int di_ridgethresh = get_ridgethresh(di_spacing); if (maxval < di_ridgethresh) di_ridgethresh = maxval; const int XINTERVAL = 1 << level; const int YINTERVAL = 1 << level; const int XHINT = XINTERVAL >> 1; const int YHINT = YINTERVAL >> 1; for (Y = YHINT; Y < YMAX; Y += YINTERVAL) { int yplus = Y+YHINT; int yminus = Y-YHINT; int y2minus = Y-YINTERVAL; if (y2minus < 0) y2minus = 0; unsigned char *py = in[Y]; unsigned char *pyplus; if (yplus == YMAX) pyplus = in[yminus]; else pyplus = in[yplus]; unsigned char *pyminus = in[yminus]; unsigned char *py2minus = in[y2minus]; v = q = pyminus[0]; r = pyplus[0]; for (X = XHINT; X < XMAX; ) { int xplus = X+XHINT; int xminus = X-XHINT; p = q; s = r; u = v; if (xplus != XMAX) { q = pyminus[xplus]; r = pyplus[xplus]; } if (!yminus) t = p; else t = py2minus[X]; prepel = py[X]; int predictor; predictor = predictors[Y][X]; ridgepel = ridge[Y][X]; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((q))-((p)))) > 0) { order = 2*27; biggest = ((q)); smallest = ((p)); } else { if (temp == 0) order = 27; biggest = ((p)); smallest = ((q)); } if ((temp=(((r))-((q)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((s))-((r)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((p))-((s)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((r)) > biggest) biggest = ((r)); else if (((r)) < smallest) smallest = ((r)); if (((s)) > biggest) biggest = ((s)); else if (((s)) < smallest) smallest = ((s)); if ((biggest - smallest < (sq_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((p))+((q))+((r))+((s))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((q))+((s)))>>1; else if (predictor == 2) estimate = (((p))+((r)))>>1; else if (predictor == 3) estimate = (((Y!=YHINT)&&(xplus!=XMAX))) ? ((py2minus)[X]) : (((p))+((q)))>>1; else estimate = (((X!=XHINT)&&(yplus!=YMAX))) ? ((py)[X-XINTERVAL]): (((p))+((s)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p)); } else if (predictor == 6) { elow = (((p))+((r)))>>1; ehigh = (((q))+((s)))>>1; int fromlow = prepel-elow; int fromhigh = prepel-ehigh; if (abt[fromlow] < abt[fromhigh]) { estimate = elow; ridgepel = 2; predictor = 2; } else { estimate = ehigh; ridgepel = 3; predictor = 1; } } else if (predictor == 7) estimate = (((q))+((r)))>>1; else if (predictor == 8) estimate = (((s))+((r)))>>1; else if (predictor == 9) estimate = (((p))+((s)))>>1; else estimate = (((q))+((p)))>>1; } else { if (predictor == 11) estimate = ((p)); else if (predictor == 12) estimate = ((r)); else if (predictor == 13) estimate = ((q)); else estimate = ((s)); } if ((Y >= truerows)||(X >= truecols)) prepel = estimate; error = prepel - estimate; code = error = (sq_encode_table)[error]; if (code < 0) code += (sq_range); if ((actuallower[estimate] > actualhigher[estimate])&&code) code = (sq_range) - code; if (code > (sq_range)/2) { code = (sq_range) - code; code *= 2; } else if (code) code = 2*code - 1; if (error > 0) actualhigher[estimate]++; else if (error < 0) actuallower[estimate]++; (prepel) = (sq_decode_table)[error] + estimate; if ((prepel) < minval) (prepel) = minval; else if ((prepel) > maxval) (prepel) = maxval; error = abt[(prepel) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-(prepel)]; if ((temp=abt[(prepel) - ((((q))+((s)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[(prepel) - ((((p))+((r)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[(prepel) - ((((q))+((r)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[(prepel) - ((((s))+((r)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[(prepel) - ((((p))+((s)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[(prepel) - ((((p))+((q)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((p))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((r))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((q))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((s))]) < error) { error = temp; predictor = 14; } if (((Y!=YHINT)&&(xplus!=XMAX)) && ((temp=offabt[((py2minus)[X])]) < error)) { error = temp; predictor = 3; } if (((X!=XHINT)&&(yplus!=YMAX)) && ((temp=offabt[((py)[X-XINTERVAL])]) < error)) { error = temp; predictor = 4; } } py[X] = prepel; scratch[Y][X] = code; ridge[Y][X] = ridgepel; predictors[Y][X] = predictor; v = prepel; prepel = pyminus[X]; predictor = predictors[yminus][X]; ridgepel = ridge[yminus][X]; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((t))-((p)))) > 0) { order = 2*27; biggest = ((t)); smallest = ((p)); } else { if (temp == 0) order = 27; biggest = ((p)); smallest = ((t)); } if ((temp=(((q))-((t)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((v))-((q)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((p))-((v)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((q)) > biggest) biggest = ((q)); else if (((q)) < smallest) smallest = ((q)); if (((v)) > biggest) biggest = ((v)); else if (((v)) < smallest) smallest = ((v)); if ((biggest - smallest < (di_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((p))+((t))+((q))+((v))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((t))+((v)))>>1; else if (predictor == 2) estimate = (((p))+((q)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((py2minus)[xminus]) : (((p))+((t)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((py2minus)[xplus]): (((p))+((v)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((p)); } else if (predictor == 6) { elow = (((p))+((q)))>>1; ehigh = (((t))+((v)))>>1; int fromlow = prepel-elow; int fromhigh = prepel-ehigh; if (abt[fromlow] < abt[fromhigh]) { estimate = elow; ridgepel = 2; predictor = 2; } else { estimate = ehigh; ridgepel = 3; predictor = 1; } } else if (predictor == 7) estimate = (((t))+((q)))>>1; else if (predictor == 8) estimate = (((v))+((q)))>>1; else if (predictor == 9) estimate = (((p))+((v)))>>1; else estimate = (((t))+((p)))>>1; } else { if (predictor == 11) estimate = ((p)); else if (predictor == 12) estimate = ((q)); else if (predictor == 13) estimate = ((t)); else estimate = ((v)); } if ((yminus >= truerows)||(X >= truecols)) prepel = estimate; error = prepel - estimate; code = error = (di_encode_table)[error]; if (code < 0) code += (di_range); if ((actuallower[estimate] > actualhigher[estimate])&&code) code = (di_range) - code; if (code > (di_range)/2) { code = (di_range) - code; code *= 2; } else if (code) code = 2*code - 1; if (error > 0) actualhigher[estimate]++; else if (error < 0) actuallower[estimate]++; (prepel) = (di_decode_table)[error] + estimate; if ((prepel) < minval) (prepel) = minval; else if ((prepel) > maxval) (prepel) = maxval; error = abt[(prepel) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-(prepel)]; if ((temp=abt[(prepel) - ((((t))+((v)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[(prepel) - ((((p))+((q)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[(prepel) - ((((t))+((q)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[(prepel) - ((((v))+((q)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[(prepel) - ((((p))+((v)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[(prepel) - ((((p))+((t)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((p))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((q))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((t))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((v))]) < error) { error = temp; predictor = 14; } if (((xminus) &&(yminus)) && ((temp=offabt[((py2minus)[xminus])]) < error)) { error = temp; predictor = 3; } if (((xplus!=XMAX)&&(yminus)) && ((temp=offabt[((py2minus)[xplus])]) < error)) { error = temp; predictor = 4; } } pyminus[X] = prepel; scratch[yminus][X] = code; ridge[yminus][X] = ridgepel; predictors[yminus][X] = predictor; prepel = py[xminus]; predictor = predictors[Y][xminus]; ridgepel = ridge[Y][xminus]; if (comp == 0) { register int temp; register int order = 0; register int biggest, smallest; if ((temp=(((p))-((u)))) > 0) { order = 2*27; biggest = ((p)); smallest = ((u)); } else { if (temp == 0) order = 27; biggest = ((u)); smallest = ((p)); } if ((temp=(((v))-((p)))) > 0) order += 2*9; else if (temp == 0) order += 9; if ((temp=(((s))-((v)))) > 0) order += 2*3; else if (temp == 0) order += 3; if ((temp=(((u))-((s)))) > 0) order += 2; else if (temp == 0) order += 1; predictor = orderarray[order]; if (((v)) > biggest) biggest = ((v)); else if (((v)) < smallest) smallest = ((v)); if (((s)) > biggest) biggest = ((s)); else if (((s)) < smallest) smallest = ((s)); if ((biggest - smallest < (di_ridgethresh))&& ((predictor < 3)||(predictor > 5))) predictor = 0; } if (!predictor) estimate = (((u))+((p))+((v))+((s))+2)>>2; else if (predictor < 5) { if (predictor == 1) estimate = (((p))+((s)))>>1; else if (predictor == 2) estimate = (((u))+((v)))>>1; else if (predictor == 3) estimate = (((xminus) &&(yminus))) ? ((pyminus)[X-XINTERVAL]) : (((u))+((p)))>>1; else estimate = (((xplus!=XMAX)&&(yminus))) ? ((pyminus)[X]): (((u))+((s)))>>1; } else if (predictor < 11) { if (predictor == 5) { estimate = ((u)); } else if (predictor == 6) { elow = (((u))+((v)))>>1; ehigh = (((p))+((s)))>>1; int fromlow = prepel-elow; int fromhigh = prepel-ehigh; if (abt[fromlow] < abt[fromhigh]) { estimate = elow; ridgepel = 2; predictor = 2; } else { estimate = ehigh; ridgepel = 3; predictor = 1; } } else if (predictor == 7) estimate = (((p))+((v)))>>1; else if (predictor == 8) estimate = (((s))+((v)))>>1; else if (predictor == 9) estimate = (((u))+((s)))>>1; else estimate = (((p))+((u)))>>1; } else { if (predictor == 11) estimate = ((u)); else if (predictor == 12) estimate = ((v)); else if (predictor == 13) estimate = ((p)); else estimate = ((s)); } if ((Y >= truerows)||(xminus >= truecols)) prepel = estimate; error = prepel - estimate; code = error = (di_encode_table)[error]; if (code < 0) code += (di_range); if ((actuallower[estimate] > actualhigher[estimate])&&code) code = (di_range) - code; if (code > (di_range)/2) { code = (di_range) - code; code *= 2; } else if (code) code = 2*code - 1; if (error > 0) actualhigher[estimate]++; else if (error < 0) actuallower[estimate]++; (prepel) = (di_decode_table)[error] + estimate; if ((prepel) < minval) (prepel) = minval; else if ((prepel) > maxval) (prepel) = maxval; error = abt[(prepel) - estimate]; if (!comp && error) { register int temp; const int *offabt = &abt[-(prepel)]; if ((temp=abt[(prepel) - ((((p))+((s)))>>1)]) < error) { error = temp; predictor = 1; } if ((temp=abt[(prepel) - ((((u))+((v)))>>1)]) < error) { error = temp; predictor = 2; } if ((temp=abt[(prepel) - ((((p))+((v)))>>1)]) < error) { error = temp; predictor = 7; } if ((temp=abt[(prepel) - ((((s))+((v)))>>1)]) < error) { error = temp; predictor = 8; } if ((temp=abt[(prepel) - ((((u))+((s)))>>1)]) < error) { error = temp; predictor = 9; } if ((temp=abt[(prepel) - ((((u))+((p)))>>1)]) < error) { error = temp; predictor = 10; } if ((temp=offabt[((u))]) < error) { error = temp; predictor = 11; } if ((temp=offabt[((v))]) < error) { error = temp; predictor = 12; } if ((temp=offabt[((p))]) < error) { error = temp; predictor = 13; } if ((temp=offabt[((s))]) < error) { error = temp; predictor = 14; } if (((xminus) &&(yminus)) && ((temp=offabt[((pyminus)[X-XINTERVAL])]) < error)) { error = temp; predictor = 3; } if (((xplus!=XMAX)&&(yminus)) && ((temp=offabt[((pyminus)[X])]) < error)) { error = temp; predictor = 4; } } py[xminus] = prepel; scratch[Y][xminus] = code; ridge[Y][xminus] = ridgepel; predictors[Y][xminus] = predictor; X += XINTERVAL; } } } // See comments in the middle of bincode for how this converts internal // representation in scratch array into a coder index and topcodeval. void mktopcode( const unsigned char scratchval, const unsigned char& l0, const unsigned char& l1, const unsigned char& l2, unsigned char rscratchval, // Not const so can force to zero const unsigned char& r0, const unsigned char& r1, const unsigned char& r2, const int pnmtype, rlcomp *codeout, const int putflag, const int sendright, const int& range) { unsigned char send0, send1, send2, rsend0, rsend1, rsend2; int coder; unsigned char topcodeval; int monochromespecialflag = 0; // monochromespecialflag is to deal with special case of bilevel // images where a monochrome topcode of 49 or 52 is very likely // on the last band send0 = l0; rsend0 = r0; if (pnmtype == 1) { rsend1 = rsend2 = send1 = send2 = 0; } else { send1 = l1; send2 = l2; rsend1 = r1; rsend2 = r2; } if (!sendright) { rsend0 = rsend1 = rsend2 = 0; rscratchval = 0; } coder = (scratchval & 3) ? 1 : 0; coder |= (scratchval & 0xc) ? 2 : 0; coder |= (scratchval & 0x30) ? 4 : 0; if (coder == 0) return; if (pnmtype != 1) { unsigned char nextcoderleft, nextcoderright; nextcoderleft = 0; nextcoderleft |= (scratchval & 2) ? 1 : 0; nextcoderleft |= (scratchval & 0x8) ? 2 : 0; nextcoderleft |= (scratchval & 0x20) ? 4 : 0; nextcoderright = 0; nextcoderright |= (rscratchval & 2) ? 1 : 0; nextcoderright |= (rscratchval & 0x8) ? 2 : 0; nextcoderright |= (rscratchval & 0x20) ? 4 : 0; topcodeval = 0; if (nextcoderleft) topcodeval |= 0x1; if (nextcoderright) topcodeval |= 0x2; if (send0) topcodeval |= 0x4; if (send1) topcodeval |= 0x10; if (send2) topcodeval |= 0x40; if (rsend0) topcodeval |= 0x8; if (rsend1) topcodeval |= 0x20; if (rsend2) topcodeval |= 0x80; } else { topcodeval = 0; topcodeval |= (scratchval & 0x2) ? 2 : 0; topcodeval |= (rscratchval & 0x2) ? 1 : 0; topcodeval <<= 4; if (send0) { if (send0 == 1) { topcodeval |= 0x4; send0 = 0; } else if (send0 == 2) { topcodeval |= 0x8; send0 = 0; } else { topcodeval |= 0xc; } } if (rsend0) { if (rsend0 == 1) { topcodeval |= 0x1; rsend0 = 0; } else if (rsend0 == 2) { topcodeval |= 0x2; rsend0 = 0; } else { topcodeval |= 0x3; } } if ((topcodeval == 49)||(topcodeval == 52)) { // Collapse the error = +1 in either left or right cases // into one: makes for efficiency for bilevel pictures monochromespecialflag = topcodeval; topcodeval = 52; } if ((topcodeval == 50)||(topcodeval == 56)) { // Might as well do the same thing for error = -1 in either // right or left, though this is relatively insignificant monochromespecialflag = (topcodeval>>1); topcodeval = 56; } } codeout[3].incorput(topcodeval,putflag); if (putflag && monochromespecialflag) { codeout[0].putbit(monochromespecialflag & 1); } if (send0){ codeout[0].incorput(send0,putflag); } if (send1){ codeout[1].incorput(send1,putflag); } if (send2){ codeout[2].incorput(send2,putflag); } if (!sendright) return; if (rsend0){ codeout[0].incorput(rsend0,putflag); } if (rsend1){ codeout[1].incorput(rsend1,putflag); } if (rsend2){ codeout[2].incorput(rsend2,putflag); } } // // bincode works from the finest band upwards, chunking zeros up the binary // tree until a non-zero entry is reached, which is marked as a leaf. It // then works down the tree, Huffman coding each band. // int bincode(unsigned char ***pyr, const int& inrows, const int& incols, rlcomp *codeout, int fromlevel, const int *spacing, const int pnmtype, const int& maxval) { int x, y; int i,j; int rpel; int scratchpel; // Deal with DPCM coding of coarsest band first for (int comp = 0; comp < pnmtype; comp++) { int predpel; int lpspacing = 1< 127) temp -= 256; codeout[0].inc(scratch[y][x] = (unsigned char) temp); } codeout[0].writetree(); for (y = 0; y < inrows; y += lpspacing) for (x = 0; x < incols; x += lpspacing){ codeout[0].put((unsigned char) scratch[y][x]); #ifdef DEBUG scratch[y][x] = 0; pyr[comp][y][x] = 0; #endif } codeout[0].reset(); } // Flags on scratch image interpreted as follows: // 0: Beyond end of a tree - don't code // 1: Tree terminator // 2: Ordinary // Bottom 2 bits channel 0, next 2 bits channel 1, next 2 bits chan 2 const int rows = inrows; const int cols = incols; int fillbyte = 0x2a; // Binary 00101010 if (pnmtype == 1) fillbyte = 2; for (y = 0; y < rows; y++) memset(scratch[y],fillbyte,cols); // First blank out highest pass int yodd = 0; for (y = 0; y < rows; y++, yodd ^= 1) { for (x = (yodd? 0:1); x < cols; x += 2) { scratchpel = scratch[y][x]; if (pyr[0][y][x] == 0) scratchpel = (scratchpel & 0x3c) | 1; if ((pnmtype > 1)&&(pyr[1][y][x] == 0)) scratchpel = (scratchpel & 0x33) | 4; if ((pnmtype > 1)&&(pyr[2][y][x] == 0)) scratchpel = (scratchpel & 0x0f) | 0x10; scratch[y][x] = scratchpel; } } // Now first mpass for (y = 1; y < rows; y += 2) for (x = 1; x < cols; x += 2) { if (((scratch[y-1][x] & 0x03) == 1)&& ((scratch[y][x-1] & 0x03) == 1)) { scratch[y][x] = (scratch[y][x] & 0x3c) | 1; scratch[y-1][x] = scratch[y-1][x] & 0x3c; scratch[y][x-1] = scratch[y][x-1] & 0x3c; } if (pnmtype == 1) continue; if (((scratch[y-1][x] & 0x0c) == 4)&& ((scratch[y][x-1] & 0x0c) == 4)) { scratch[y][x] = (scratch[y][x] & 0x33) | 4; scratch[y-1][x] = scratch[y-1][x] & 0x33; scratch[y][x-1] = scratch[y][x-1] & 0x33; } if (((scratch[y-1][x] & 0x30) == 0x10)&& ((scratch[y][x-1] & 0x30) == 0x10)) { scratch[y][x] = (scratch[y][x] & 0x0f) | 0x10; scratch[y-1][x] = scratch[y-1][x] & 0x0f; scratch[y][x-1] = scratch[y][x-1] & 0x0f; } } // Now remainder of bands for (i = 2; i <= 2*fromlevel; i *= 2) { int twoi = i<<1; int halfi = i>>1; yodd = 0; for (y = 0; y < rows; y += i, yodd ^= 1) { int lchild = yodd? halfi : -halfi; for (x = (yodd? 0: i); x < cols; x += twoi) { if (((scratch[y+halfi][x+lchild] & 0x03) == 1)&& (pyr[0][y+halfi][x+lchild] == 0)&& ((scratch[y+halfi][x+lchild+i] & 0x03) == 1)&& (pyr[0][y+halfi][x+lchild+i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x3c) | 1; scratch[y+halfi][x+lchild] &= 0x3c; scratch[y+halfi][x+lchild+i] &= 0x3c; } if (pnmtype == 1) continue; if (((scratch[y+halfi][x+lchild] & 0x0c) == 4)&& (pyr[1][y+halfi][x+lchild] == 0)&& ((scratch[y+halfi][x+lchild+i] & 0x0c) == 4)&& (pyr[1][y+halfi][x+lchild+i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x33) | 4; scratch[y+halfi][x+lchild] &= 0x33; scratch[y+halfi][x+lchild+i] &= 0x33; } if (((scratch[y+halfi][x+lchild] & 0x30) == 0x10)&& (pyr[2][y+halfi][x+lchild] == 0)&& ((scratch[y+halfi][x+lchild+i] & 0x30) == 0x10)&& (pyr[2][y+halfi][x+lchild+i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x0f) | 0x10; scratch[y+halfi][x+lchild] &= 0x0f; scratch[y+halfi][x+lchild+i] &= 0x0f; } } } for (y = i; y < rows; y += twoi) for (x = i; x < cols; x += twoi) { if(((scratch[y-i][x] & 0x03) == 1)&& (pyr[0][y-i][x] == 0)&& ((scratch[y][x-i] & 0x03) == 1)&& (pyr[0][y][x-i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x3c) | 1; scratch[y-i][x] &= 0x3c; scratch[y][x-i] &= 0x3c; } if (pnmtype == 1) continue; if(((scratch[y-i][x] & 0x0c) == 4)&& (pyr[1][y-i][x] == 0)&& ((scratch[y][x-i] & 0x0c) == 4)&& (pyr[1][y][x-i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x33) | 4; scratch[y-i][x] &= 0x33; scratch[y][x-i] &= 0x33; } if(((scratch[y-i][x] & 0x30) == 0x10)&& (pyr[2][y-i][x] == 0)&& ((scratch[y][x-i] & 0x30) == 0x10)&& (pyr[2][y][x-i] == 0)) { scratch[y][x] = (scratch[y][x] & 0x0f) | 0x10; scratch[y-i][x] &= 0x0f; scratch[y][x-i] &= 0x0f; } } } // Finally, change all the terminators in the highest band back // to zeros yodd = 0; for (y = 0; y < rows; y++, yodd ^= 1) { for (x = (yodd? 0:1); x < cols; x += 2) { scratchpel = scratch[y][x]; if (scratchpel & 1) scratchpel ^= 3; // I.e. back to 2: ordinary if (scratchpel & 4) scratchpel ^= 0xc; if (scratchpel & 0x10) scratchpel ^= 0x30; scratch[y][x] = scratchpel; } } // Now interpret the codes and send them in the following way: // From the scratch image determine which coders are still running and // convert this to a 3-bit number which will index the array of codeout // objects. Then form a "topcode" output word where the bits have the // meanings: bit 0: if 1, have to send children in channel 0; if 0 this is // either a tree termination or below a tree termination. bit 1: if 1, have // to send children in channel 1; bit 2: if 2, have to send children in channel // 2. Therefore the bit pattern in the lowest three bits corresponds to the // coder index for the following band. bit 4: if 1, channel 0 value is non-zero. // bit 5: if 1, channel 1 value is non-zero. bit 6; if 1, channel 2 value // is non-zero. int band = 1; for (i = fromlevel; i > 0; i--, band += 2) { #ifdef DEBUG printf("LEVEL %d, band %d\n", i, band); #endif prepare_encoder(spacing[band], spacing[band+1], maxval); int hoffset = 1<<(i-1); int offset = 1<= cols) // break; mktopcode(scratch[y-hoffset][x+offset], pyr[0][y-hoffset][x+offset], pyr[1][y-hoffset][x+offset], pyr[2][y-hoffset][x+offset], scratch[y][x+hoffset], pyr[0][y][x+hoffset], pyr[1][y][x+hoffset], pyr[2][y][x+hoffset], pnmtype, codeout+DST, incorput, 1, di_arr[i]); if (incorput) { if ((rpel = ridge[y-hoffset][x+offset]) != 0) codeout[0].putbit(rpel - 2); if ((rpel = ridge[y][x+hoffset]) != 0) codeout[0].putbit(rpel - 2); } } } if (!incorput) { // First time only for (j = 0; j < numcoders; j++) { codeout[j].writetree(); } } } for (j = 0; j < numcoders; j++) codeout[j].reset(); } return 0; } void enc_plane(unsigned char **in, const int& xmax, const int& ymax, const int& fromlevel, const int& tolevel, const int *spacing, rlcomp *codeout, const int& minval, const int& maxval, const int& comp, const int& truecols, const int& truerows) { #ifdef DEBUG static char planefile[7] = {'p','l','a','n','e','0','\0'}; static char recfile[7] = {'r','e','c','o','v','0','\0'}; #endif int actualhigher[256]; int actuallower[256]; for (int act = 0; act < 256; act++) actualhigher[act] = actuallower[act] = 0; // First put lowband into scratch int lpspacing = 1< tolevel; level--, band += 2) { enc_level(in,xmax,ymax,level,spacing[band], spacing[band+1], minval, maxval, comp, truecols, truerows, actuallower, actualhigher); } #ifdef DEBUG writepgm(recfile,in,ymax,xmax,255); #endif // Now copy encoded version back into in unsigned char *pfrom, *pto; for (y = 0; y < ymax; y++) { pto = in[y]; pfrom = scratch[y]; for (int x = 0; x < xmax; x++) *pto++ = *pfrom++; } // bincode(pyr, ymax, xmax, codeout, in, fromlevel, tolevel, // spacing); #ifdef DEBUG writepgm(planefile,debugimage,ymax,xmax,255); ++(planefile[5]); ++(recfile[5]); /* for (int act = minval; act <= maxval; act++) printf("%d: lower = %d, higher = %d\n", act, actuallower[act], actualhigher[act]); */ #endif } int eight_bit_encode(unsigned char ***incomponents, FILE *cfp, int rows, int cols, int pnmtype, int quality, const char *comment, cfileio& fio) { unsigned char **components[3]; int maxval[3], minval[3]; int tmaxval[3], tminval[3]; // max and min of transformed versions int omaxval, ominval; // overall max and min of trans int dynamicrange; int i; int compnum; int bilevel = 0; char coltype = 'M'; // monochrome // Make ymax equal to next highest multiple of blocksize int ymax = ((rows+(1<>enc_numlevels)<>(enc_numlevels+1))<<(enc_numlevels+1); if ((xmax == cols)&&(ymax == rows)){ // Already good size. components[0] = incomponents[0]; // Compress in place components[1] = incomponents[1]; components[2] = incomponents[2]; } else pad(incomponents, components, rows, cols, pnmtype); gettrueminmax(components, pnmtype, minval, maxval,rows,cols); if (quality == 100) from_bilevel(components, pnmtype, minval, maxval, ymax, xmax, bilevel); int spacing[65]; // Quantizer spacings int transform[9]; if ((pnmtype == 3)&&(quality < 100)) { coltype = 'Y'; // rgb_ycc(components,ymax,xmax,1); calc_colour_transform(components,transform,rows,cols); rgb_pca(components,transform,ymax,xmax); order_filter(components, ymax, xmax, pnmtype, quality); gettrueminmax(components, pnmtype, tminval, tmaxval, rows,cols); } else { for (int i = 0; i < pnmtype; i++) { tminval[i] = minval[i]; tmaxval[i] = maxval[i]; } if (pnmtype == 3) coltype = 'G'; // GRB else if (quality < 100) order_filter(components, ymax, xmax, pnmtype, quality); } omaxval = tmaxval[0]; ominval = tminval[0]; if (pnmtype == 3) { if (tmaxval[1] > omaxval) omaxval = tmaxval[1]; if (tminval[1] < ominval) ominval = tminval[1]; if (tmaxval[2] > omaxval) omaxval = tmaxval[2]; if (tminval[2] < ominval) ominval = tminval[2]; } if (bilevel) { omaxval = 1; ominval = 0; } dynamicrange = omaxval - ominval; if (calcq(dynamicrange, quality, spacing+1, enc_numlevels*2) < 0) { return -1; } if (bilevel) { if (pnmtype == 1) coltype = 'B'; // Bilevel else coltype = 'C'; // Colour graphics bilevel } // Write header if (pnmtype == 1) fprintf(cfp,"1%c%c%c0\n%d\n%d\n", enc_numlevels+'0', numcoders+'0', coltype, rows, cols); else fprintf(cfp,"3%c%c%c\n%d\n%d\n", enc_numlevels+'0', numcoders+'0', coltype, rows, cols); if (comment) fprintf(cfp,"%s",comment); fputc(0,cfp); // String terminator if (coltype == 'Y') fprintf(cfp,"%d %d %d %d %d %d %d %d %d\n", transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], transform[6], transform[7], transform[8]); // Output all the quantizer spacing parameters for (i = 1; i <=enc_numlevels*2; i++) fputc((unsigned char) spacing[i], cfp); // And the minval and maxval parameters for (i = 0; i < pnmtype; i++) { fputc((unsigned char) minval[i], cfp); fputc((unsigned char) maxval[i], cfp); } fputc((unsigned char) ominval, cfp); fputc((unsigned char) omaxval, cfp); // Set up the huffman coders rlcomp codeout[numcoders]; for (i = 0; i < numcoders; i++) { // codeout[i].init(); codeout[i].encodelength(xmax*ymax); // Try to improve on // memory usage later. codeout[i].attach(&fio); codeout[i].indexnumber(i); // For debugging #ifdef DEBUG codeout[i].debugon(); // For debugging #endif } // Encode each plane of the picture makescratch(xmax, ymax); for (compnum = 0; compnum < pnmtype; compnum++) { enc_plane(components[compnum],xmax,ymax,enc_numlevels, 0, spacing, codeout, ominval, omaxval, compnum, cols, rows); } bincode(components, ymax, xmax, codeout, enc_numlevels, spacing, pnmtype, omaxval); fio.flush(); // And finally, clean up if (components[0] != incomponents[0]) { for (compnum = 0; compnum < pnmtype; compnum++) { unsigned char **in = components[compnum]; for (i = 0; i < ymax; i++) delete [] in[i]; delete [] in; } } deletescratch(ymax); return 1; } int apt_encode(unsigned char ***incomponents, FILE *cfp, int rows, int cols, int pnmtype, int quality, const char *comment, const int numbits) { const int encoder_subversion = 1; cfileio fio(cfp); fprintf(cfp,"apt1.0%c%c%c%c",'0'+encoder_subversion, '0'+pnmtype,'0'+ numbits,quality); bool candopalettize = 0; if (numbits <= 8) { if (pnmtype == 3) { palettize pal(incomponents, rows, cols, quality, candopalettize); if (candopalettize) { fputc('P',cfp); // Palettize flag quality = 100; pal.writepalette(cfp,fio); } else fputc('R',cfp); // Raw = unpalettized flag } else { bwpalettize pal(incomponents, rows, cols, quality, candopalettize); if (candopalettize) { fputc('p',cfp); // Palettize flag quality = 100; pal.writepalette(cfp,fio); } else fputc('R',cfp); // Raw = unpalettized flag } } else fputc('R',cfp); // Raw = unpalettized flag if (numbits <= 8) { // order_filter(incomponents, rows, cols, pnmtype, quality); return eight_bit_encode(incomponents, cfp, rows, cols, pnmtype, quality, comment,fio); } // Have to reorganise 16-bit picture into 2 8-bit pictures // Do this so that each line of the picture is in two halves unsigned char *line = new unsigned char[cols*2]; int pelval; int compnum; for (compnum = 0; compnum < pnmtype; compnum++) { for (int i = 0; i < rows; i++) { unsigned char *inp = incomponents[compnum][i]; unsigned char *ap = line; unsigned char *bp = line+cols; for (int j = 0; j < cols; j++) { pelval = *inp++ << 8; pelval |= *inp++; *ap++ = (pelval>>(numbits-8)) & 0xff; *bp++ = pelval & ((1<<(numbits-8)) - 1); } memcpy(incomponents[compnum][i],line,cols*2); } } delete [] line; // Do the most significant bits first int retval = eight_bit_encode(incomponents, cfp, rows, cols, pnmtype, quality, comment,fio); if ((retval < 0)||(quality < 100)) return retval; // Code less significant bits // Move the line pointers to the second halves of the line for (compnum = 0; compnum < pnmtype; compnum++) { for (int i = 0; i < rows; i++) { incomponents[compnum][i] += cols; } } retval = eight_bit_encode(incomponents, cfp, rows, cols, pnmtype, quality, "",fio); // Move the line pointers back. Probably unnecessary, but don't // assume the caller won't use them again. for (compnum = 0; compnum < pnmtype; compnum++) { for (int i = 0; i < rows; i++) { incomponents[compnum][i] -= cols; } } return retval; } }; /* CLIP_I */ // // CLIP interface to picture files. Reads PNM, BMP, JPEG and APT. // Writes PNM, BMP and APT. // // The methods dealing with reading and writing pictures // namespace clip_internal { // Just the declaration of clip_bmp_decode here. The definition is // below with the bmp writing functions int clip_bmp_decode(unsigned char ***components, const char *name, int& rows, int& cols, int& type); void clip_bmp_finish(unsigned char ***components, int rows, int cols, int type) { for (int compnum = 0; compnum < type; compnum++) { unsigned char **out = components[compnum]; delete [] out; // Only have to do this because bmp_decode uses a single // new per component } } }; // clip_internal namespace template int picture_of_::construct_or_read_from_file(const char *pgmname, const int bord, const int readflag) // input pgm picture { using apt1_0::apt_decode; using apt1_0::apt_finish; using clip_internal::clip_jpeg_decode; using clip_internal::clip_jpeg_finish; using clip_internal::clip_bmp_decode; using clip_internal::clip_bmp_finish; int low, high, scale; scale = 255; // Default values for scale and low low = 0; unsigned char **components[3]; char *comment = ""; int type; int maxval = 255; int cols, rows; int isclippic = 0; int isjpg = -1; int isapt = -1; int isbmp = -1; FILE *cfp = fopen(pgmname,"rb"); int bytesperpel = 1; if (!cfp) { cerr << "Can't open " << pgmname << endl; return -1; } isapt = apt_decode(components,cfp,rows, cols,type, maxval, &comment, bytesperpel); fclose(cfp); if (isapt < 0) isjpg = clip_jpeg_decode(components, pgmname, rows, cols, type); if ((isapt < 0) && (isjpg < 0)) isbmp = clip_bmp_decode(components, pgmname, rows, cols, type); if ((isapt >= 0)||(isjpg >= 0)||(isbmp >= 0)) { if (!readflag) init_new(rows,cols,bord); else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << pgmname; cerr << " into an existing picture of different size\n"; return -1; } if (type == 3){ // It's in colour // Ignore comment in this case - conversion to monochrome if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) pr->buf[i][j] = (PEL)(((int)components[0][i][j] + components[1][i][j] + components[2][i][j])/3); } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pelval; pelval = components[0][i][j] + components[1][i][j] + components[2][i][j]; pelval *= 256; pelval += components[0][i][j+1] + components[1][i][j+1] + components[2][i][j+1]; pelval /= 3; pr->buf[i][oj] = (PEL)(pelval/3); } } } else { if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { pr->buf[i][j] = (PEL) components[0][i][j]; } } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pelval; pelval = components[0][i][j]; pelval *= 256; pelval += components[0][i][j+1]; pr->buf[i][oj] = (PEL) pelval; } } } if (isapt >= 0) apt_finish(components,rows,cols,type); else if (isbmp >= 0) clip_bmp_finish(components,rows,cols,type); else clip_jpeg_finish(components,rows,cols,type); return 0; } char cbuf[256]; ifstream in(pgmname,ios::binary); if (!in) { cerr << "Cannot open " << pgmname << '\n'; return -1; } in.getline(cbuf,8); if (strcmp(cbuf,"P5")) { if (strcmp(cbuf,"P6")) { cerr << pgmname << " is not a readable picture file\n"; return -1; } // Else it's PPM in.close(); colour_picture_of_ temp(pgmname); // temp.makemonochrome(); rows = temp.red.nrows(); cols = temp.red.ncols(); if (!readflag) init_new(rows, cols, bord); else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << pgmname; cerr << " into an existing picture of different size\n"; return -1; } for (int i = 0; i < rows; i++) { PEL *pout = pr->buf[i]; PEL *pg = temp.green[i]; PEL *pr = temp.red[i]; PEL *pb = temp.blue[i]; for (int j = 0; j < cols; j++) { *pout++ = (PEL)(((int)*pg++ + *pr++ + *pb++)/3); } } return 0; } unsigned char c; c = in.peek(); while (c == '#') // Comment line { // Is it a cLiP file? in >> c; // Gets the # in >> cbuf; // Gets the next word if (strcmp(cbuf,"cLiP")) // Not CLIP file { in.getline(cbuf,256); // Absorb comment line } else { isclippic = 1; in >> cbuf; if (!strcmp(cbuf, "quantized")) // get low and high values { in >> low; in >> high; in.getline(cbuf,256); // Absorb rest scale = high - low; if (scale == 0) scale = 1; } else if (!strcmp(cbuf, "shifted")) { in >> low; in >> high; in.getline(cbuf, 256); // scale is unaffected } else { in.getline(cbuf,256); // Absorb rest of comment line // and treat as non-CLIP isclippic = 0; } } c = in.peek(); } in >> cols >> rows >> maxval; in.getline(cbuf,8); // absorbs newline if (!readflag) init_new(rows, cols, bord); else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << pgmname; cerr << " into an existing picture of different size\n"; return -1; } int zerocode = (-low*255 + scale/2)/scale; if (maxval <= 255) { // 8 bit PGM if (isclippic) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); if ((int) c == zerocode) pr->buf[i][j] = 0; // Everything in 0's bin, gets mapped to 0 else pr->buf[i][j]=(((PEL)c)*scale+(255/2))/255+low; } } else { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); pr->buf[i][j] = (PEL) c; } } } else { // 16 bit PGM PEL pelval; if (isclippic) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; if (pelval == zerocode) pr->buf[i][j] = 0; // Everything in 0's bin, gets mapped to 0 else pr->buf[i][j]=(pelval*scale+(255/2))/255+low; } } else { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; pr->buf[i][j] = pelval; } } } in.close(); return 0; } template int picture_of_::read(const char *pgmname) { return construct_or_read_from_file(pgmname); } template int picture_of_::write(const char *pgmname) // Write picture to "P5" pgm format - shifted/quantized to 256 or 256*256 levels { ofstream out(pgmname,ios::binary); int rows = rowrange.length(); int cols = colrange.length(); int low, high, scale; int round; int dummyr, dummyc; if (!out) { cerr << "Cannot open " << pgmname << '\n'; return -1; } low = (int) min(dummyr, dummyc); high = (int) max(dummyr, dummyc); int mode = 0; // = 0 8 bit unquantized // = 1 8 bit shifted // = 2 8 bit quantized // = 3 16 bit unquantized // = 4 16 bit shifted scale = high - low; if (scale == 0) scale = 1; round = scale/2; if (scale > 256*256 - 1) mode = 2; // Have to quantize else if (scale > 255) { if ((low >= 0)&&(high <= 256*256 - 1)) mode = 3; else mode = 4; } else { if ((low >= 0)&&(high <= 255)) mode = 0; else mode = 1; } if ((mode == 0)||(mode == 3)) { out << "P5\n# cLiP unquantized\n"; out << cols << ' ' << rows << '\n' << high << '\n'; low = 0; } else if ((mode == 1)||(mode == 4)) { out << "P5\n# cLiP shifted "; out << low << ' ' << high << '\n'; out << cols << ' ' << rows << "\n" << scale << "\n"; } else { out << "P5\n# cLiP quantized "; out << low << ' ' << high << '\n'; out << cols << ' ' << rows << "\n255\n"; } if ((mode == 0)||(mode == 1)) { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) out.put((unsigned char) (pr->buf[i][j]-low)); } } else if ((mode == 3)||(mode == 4)) { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) { int pelval = (int)(pr->buf[i][j] - low); out.put((unsigned char)(pelval / 256)); out.put((unsigned char)(pelval % 256)); } } } else { for (int i = rowrange.istart(); i <= rowrange.iend(); i += rowrange.ispacing()) { for (int j = colrange.istart(); j <= colrange.iend(); j += colrange.ispacing()) out.put((unsigned char) ((round+(pr->buf[i][j]-low)*255)/scale)); } } out.close(); return 0; } // // The methods dealing with reading and writing colour pictures // template int colour_picture_of_::construct_or_read_from_file(const char *ppmname, const int bord, const int readflag) // input ppm picture { using apt1_0::apt_decode; using apt1_0::apt_finish; using clip_internal::clip_jpeg_decode; using clip_internal::clip_jpeg_finish; using clip_internal::clip_bmp_decode; using clip_internal::clip_bmp_finish; char cbuf[256]; if (!readflag) qxp = 0; int cols, rows; int rlow, rhigh, rscale; int glow, ghigh, gscale; int blow, bhigh, bscale; rscale = 255; // Default values for scale and low gscale = 255; // Default values for scale and low bscale = 255; // Default values for scale and low rlow = 0; glow = 0; blow = 0; unsigned char **components[3]; char *comment = ""; int type; int maxval = 255; int isclippic = 0; int isjpg = -1; int isapt = -1; int isbmp = -1; FILE *cfp = fopen(ppmname,"rb"); int bytesperpel = 1; if (!cfp) { cerr << "Can't open " << ppmname << endl; return -1; } isapt = apt_decode(components,cfp,rows, cols,type, maxval, &comment, bytesperpel); fclose(cfp); if (isapt < 0) isjpg = clip_jpeg_decode(components, ppmname, rows, cols, type); if ((isapt < 0) && (isjpg < 0)) isbmp = clip_bmp_decode(components, ppmname, rows, cols, type); if ((isapt >= 0)||(isjpg >= 0)||(isbmp >= 0)) { if (type == 3){ // It's in colour // Have to insert comment handling here if (!readflag) { red.initialize(rows, cols, bord); green.initialize(rows, cols, bord); blue.initialize(rows, cols, bord); mono.initialize(rows, cols, bord); } else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << ppmname; cerr << " into an existing picture of different size\n"; return -1; } if (readflag && (puremono == 1)) { // Have to read in as monochrome if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { int pelval; pelval = components[0][i][j]; pelval += components[1][i][j]; pelval += components[2][i][j]; mono.pr->buf[i][j] = (PEL)(pelval/3); } } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pg, pr, pb; pg = components[0][i][j]; pg *= 256; pg +=components[0][i][j+1]; pr = components[1][i][j]; pr *= 256; pr +=components[1][i][j+1]; pb = components[2][i][j]; pb *= 256; pb +=components[2][i][j+1]; mono.pr->buf[i][oj] = (PEL)((pg+pr+pb)/3); } } } else { puremono = 0; if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { green.pr->buf[i][j] = (PEL) components[0][i][j]; red.pr->buf[i][j] = (PEL) components[1][i][j]; blue.pr->buf[i][j] = (PEL) components[2][i][j]; } } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pelval; pelval = components[0][i][j]; pelval *= 256; pelval +=components[0][i][j+1]; green.pr->buf[i][oj] = (PEL) pelval; pelval = components[1][i][j]; pelval *= 256; pelval +=components[1][i][j+1]; red.pr->buf[i][oj] = (PEL) pelval; pelval = components[2][i][j]; pelval *= 256; pelval +=components[2][i][j+1]; blue.pr->buf[i][oj] = (PEL) pelval; } } } } else { // Ignore comment in this case - conversion from monochrome if (!readflag) { mono.initialize(rows, cols, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); } else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << ppmname; cerr << " into an existing picture of different size\n"; return -1; } if (readflag && (puremono == 0)) { if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { green.pr->buf[i][j] = (PEL) components[0][i][j]; red.pr->buf[i][j] = (PEL) components[0][i][j]; blue.pr->buf[i][j] = (PEL) components[0][i][j]; } } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pelval; pelval = components[0][i][j]; pelval *= 256; pelval +=components[0][i][j+1]; green.pr->buf[i][oj] = (PEL)pelval; red.pr->buf[i][oj] = (PEL)pelval; blue.pr->buf[i][oj] = (PEL)pelval; } } } else { puremono = 1; if (bytesperpel == 1) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) mono.pr->buf[i][j] = (PEL) components[0][i][j]; } else { for (int i = 0; i < rows; i++) for (int j = 0, oj = 0; oj < cols; j += 2, oj++) { int pelval; pelval = components[0][i][j]; pelval *= 256; pelval += components[0][i][j+1]; mono.pr->buf[i][oj] = (PEL) pelval; } } } } if (isapt >= 0) apt_finish(components,rows,cols,type); else if (isbmp >= 0) clip_bmp_finish(components,rows,cols,type); else clip_jpeg_finish(components, rows, cols, type); return 0; } ifstream in(ppmname,ios::binary); if (!in) { cerr << "Cannot open " << ppmname << '\n'; return -1; } in.getline(cbuf,8); if (strcmp(cbuf,"P6")) { if (strcmp(cbuf,"P5")) { cerr << ppmname << " is not a readable picture file\n"; return -1; } // Else it's PGM in.close(); picture_of_ temp(ppmname); rows = temp.nrows(); cols = temp.ncols(); if (!readflag) { mono.initialize(rows, cols, bord); red.fake_initialize(mono); green.fake_initialize(mono); blue.fake_initialize(mono); puremono = 1; mono = temp; } else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << ppmname; cerr << " into an existing picture of different size\n"; return -1; } else if (puremono) mono = temp; else { // Monochrome pic read into colour_picture_of_ initialized // as colour. Can't change puremono state, so: red = temp; green = temp; blue = temp; } return 0; } unsigned char c; c = in.peek(); while (c == '#') // Comment line { // Is it a cLiP file? in >> c; // Gets the # in >> cbuf; // Gets the next word if (strcmp(cbuf,"cLiP")) // Not CLIP file { in.getline(cbuf,256); // Absorb comment line } else { isclippic = 1; in >> cbuf; if (!strcmp(cbuf, "quantized")) // get low and high values { in >> rlow >> glow >> blow; in >> rhigh >> ghigh >> bhigh; in.getline(cbuf,256); // Absorb rest rscale = rhigh - rlow; if (rscale == 0) rscale = 1; gscale = ghigh - glow; if (gscale == 0) gscale = 1; bscale = bhigh - blow; if (bscale == 0) bscale = 1; } else if (!strcmp(cbuf, "shifted")) { in >> rlow >> glow >> blow; in >> rhigh >> ghigh >> bhigh; in.getline(cbuf, 256); // scale is unaffected } else { in.getline(cbuf,256); // Absorb rest of comment line // and treat as non-CLIP isclippic = 0; } } c = in.peek(); } in >> cols >> rows >> maxval; in.getline(cbuf,8); // absorbs newline if (!readflag) { red.initialize(rows, cols, bord); green.initialize(rows, cols, bord); blue.initialize(rows, cols, bord); mono.initialize(rows, cols, bord); } else if ((rows!=nrows())||(cols!=ncols())) { cerr << "Cannot read " << ppmname; cerr << " into an existing picture of different size\n"; return -1; } puremono = 0; int rzerocode = (-rlow*255 + rscale/2)/rscale; int gzerocode = (-glow*255 + gscale/2)/gscale; int bzerocode = (-blow*255 + bscale/2)/bscale; if (maxval <= 255) { // 8 bits PPM if (isclippic) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); if ((int) c == rzerocode) red.pr->buf[i][j] = 0; else red.pr->buf[i][j] = (((PEL) c)*rscale+(255/2))/255 + rlow; c = in.get(); if ((int) c == gzerocode) green.pr->buf[i][j] = 0; else green.pr->buf[i][j] = (((PEL) c)*gscale+(255/2))/255 + glow; c = in.get(); if ((int) c == bzerocode) blue.pr->buf[i][j] = 0; else blue.pr->buf[i][j] = (((PEL) c)*bscale+(255/2))/255 + blow; } } else { // Not clippic for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); red.pr->buf[i][j] = (PEL) c; c = in.get(); green.pr->buf[i][j] = (PEL) c; c = in.get(); blue.pr->buf[i][j] = (PEL) c; } } } else { // 16 bit PPM if (isclippic) { PEL pelval; for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; if (pelval == rzerocode) red.pr->buf[i][j] = 0; else red.pr->buf[i][j] = ((pelval)*rscale+(255/2))/255 + rlow; c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; if (pelval == gzerocode) green.pr->buf[i][j] = 0; else green.pr->buf[i][j] = ((pelval)*gscale+(255/2))/255 + glow; c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; if (pelval == bzerocode) blue.pr->buf[i][j] = 0; else blue.pr->buf[i][j] = ((pelval)*bscale+(255/2))/255 + blow; } } else { // Not clippic PEL pelval; for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; red.pr->buf[i][j] = pelval; c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; green.pr->buf[i][j] = pelval; c = in.get(); pelval = ((PEL) c) * 256; c = in.get(); pelval += c; blue.pr->buf[i][j] = pelval; } } } in.close(); return 0; } template int colour_picture_of_::read(const char *ppmname) { return construct_or_read_from_file(ppmname); } template int colour_picture_of_::write(const char *ppmname) // Write picture to "P6" ppm format - shifted/quantized to 256 or 256*256 levels { if (puremono) { return mono.write(ppmname); } ofstream out(ppmname,ios::binary); int rows = red.rowrange.length(); int cols = red.colrange.length(); int rlow, rhigh; int glow, ghigh; int blow, bhigh; int low, high, scale; int dummyr, dummyc; if (!out) { cerr << "Cannot open " << ppmname << '\n'; return -1; } low = rlow = (int) red.min(dummyr, dummyc); high = rhigh = (int) red.max(dummyr, dummyc); glow = (int) green.min(dummyr, dummyc); ghigh = (int) green.max(dummyr, dummyc); if (glow < low) low = glow; if (ghigh > high) high = ghigh; blow = (int) blue.min(dummyr, dummyc); bhigh = (int) blue.max(dummyr, dummyc); if (blow < low) low = blow; if (bhigh > high) high = bhigh; int mode = 0; // = 0 8 bit unquantized // = 1 8 bit shifted // = 2 8 bit quantized // = 3 16 bit unquantized // = 4 16 bit shifted scale = high - low; if (scale == 0) scale = 1; if (scale > 256*256 - 1) mode = 2; // Have to quantize else if (scale > 255) { if ((low >= 0)&&(high <= 256*256 - 1)) mode = 3; else mode = 4; } else { if ((low >= 0)&&(high <= 255)) mode = 0; else mode = 1; } if ((mode == 0)||(mode == 3)) { out << "P6\n# cLiP unquantized\n"; out << cols << ' ' << rows << '\n' << high << '\n'; rlow = glow = blow = 0; } else if ((mode == 1)||(mode == 4)) { out << "P6\n# cLiP shifted "; out << rlow << ' ' << glow << ' ' << blow<< ' '; out << rhigh << ' ' << ghigh << ' ' << bhigh<< '\n'; out << cols << ' ' << rows << "\n" << scale << "\n"; } else { out << "P6\n# cLiP quantized "; out << rlow << ' ' << glow << ' ' << blow<< ' '; out << rhigh << ' ' << ghigh << ' ' << bhigh<< '\n'; out << cols << ' ' << rows << "\n255\n"; } if ((mode == 0)||(mode == 1)) { for (int i = red.rowrange.istart(); i <= red.rowrange.iend(); i += red.rowrange.ispacing()) { for (int j = red.colrange.istart(); j <= red.colrange.iend(); j += red.colrange.ispacing()) { out.put((unsigned char)(red.pr->buf[i][j]-rlow)); out.put((unsigned char)(green.pr->buf[i][j]-glow)); out.put((unsigned char)(blue.pr->buf[i][j]-blow)); } } } else if ((mode == 3)||(mode == 4)) { for (int i = red.rowrange.istart(); i <= red.rowrange.iend(); i += red.rowrange.ispacing()) { for (int j = red.colrange.istart(); j <= red.colrange.iend(); j += red.colrange.ispacing()) { int pelval; pelval = red.pr->buf[i][j] - rlow; out.put((unsigned char)(pelval>>8)); out.put((unsigned char)(pelval & 0xff)); pelval = green.pr->buf[i][j] - glow; out.put((unsigned char)(pelval>>8)); out.put((unsigned char)(pelval & 0xff)); pelval = blue.pr->buf[i][j] - blow; out.put((unsigned char)(pelval>>8)); out.put((unsigned char)(pelval & 0xff)); } } } else { int rscale = rhigh - rlow; int gscale = ghigh - glow; int bscale = bhigh - blow; if (rscale == 0) rscale = 1; if (gscale == 0) gscale = 1; if (bscale == 0) bscale = 1; int rround = rscale/2; int ground = gscale/2; int bround = bscale/2; for (int i = red.rowrange.istart(); i <= red.rowrange.iend(); i += red.rowrange.ispacing()) { for (int j = red.colrange.istart(); j <= red.colrange.iend(); j += red.colrange.ispacing()) { out.put((unsigned char) ((rround+(red.pr->buf[i][j]-rlow)*255)/rscale)); out.put((unsigned char) ((ground+(green.pr->buf[i][j]-glow)*255)/gscale)); out.put((unsigned char) ((bround+(blue.pr->buf[i][j]-blow)*255)/bscale)); } } } out.close(); return 0; } using apt1_0::apt_encode; template int picture_of_::aptwrite(const char *aptname, const int quality) { // Write to apt format file int rows = rowrange.length(); int cols = colrange.length(); int low, high; int dummyr, dummyc; low = min(dummyr, dummyc); high = max(dummyr, dummyc); int numbytes = 1; if (high > 255) numbytes = 2; if ((low < 0)||(high > 256*256 - 1)) { cerr << "The picture is outside limits [0,256*256-1], so"; cerr << " you cannot write to an APT file.\n)"; cerr << "Use picture_of_::write() not picture_of_::aptwrite()\n"; return -1; } int i,j; unsigned char **components[3]; unsigned char **buf; unsigned char *pbuf; components[0] = buf = new unsigned char *[rows]; components[1] = buf; components[2] = buf; // So all three components point at same for (i = 0; i < rows; i++) { pbuf = buf[i] = new unsigned char[cols*numbytes]; if (numbytes == 2) { for (j = 0; j < cols; j++) { *pbuf++ = ((int)pr->buf[i][j] >> 8) & 0xff; *pbuf++ = ((int)pr->buf[i][j] & 0xff); } } else { for (j = 0; j < cols; j++) *pbuf++ = pr->buf[i][j]; } } FILE *cfp = fopen(aptname,"wb"); if (cfp == 0) { cerr << "Cannot open " << aptname << '\n'; return -1; } int retval = apt_encode(components, cfp, rows, cols, 1, quality, "cLiP", numbytes); switch (retval) { case -1: cerr << "APT quality parameter must be between 0 and 100\n"; break; case -2: cerr << "Can't open " << aptname << endl; break; default: // Success break; } unsigned char **in = components[0]; for (int ii = 0; ii < rows; ii++) delete [] in[ii]; delete [] in; fclose(cfp); return retval; } template int colour_picture_of_::aptwrite(const char *aptname, const int quality) { // Write to apt format file if (puremono) { return mono.aptwrite(aptname, quality); } int rows = red.rowrange.length(); int cols = red.colrange.length(); int rlow, rhigh; int glow, ghigh; int blow, bhigh; int low, high; int dummyr, dummyc; low = rlow = (int) red.min(dummyr, dummyc); high = rhigh = (int) red.max(dummyr, dummyc); glow = (int) green.min(dummyr, dummyc); ghigh = (int) green.max(dummyr, dummyc); if (glow < low) low = glow; if (ghigh > high) high = ghigh; blow = (int) blue.min(dummyr, dummyc); bhigh = (int) blue.max(dummyr, dummyc); if (blow < low) low = blow; if (bhigh > high) high = bhigh; int numbytes = 1; if (high > 255) numbytes = 2; if ((low < 0)||(high > 256*256 - 1)) { cerr << "The picture is outside limits [0,256*256-1], so"; cerr << " you cannot write to an APT file.\n)"; cerr << "Use colour_picture_of_::write() not colour_picture_of_::aptwrite()\n"; return -1; } int i,j; unsigned char **components[3]; unsigned char **bufr, **bufg, **bufb; unsigned char *pr, *pg, *pb; components[0] = bufg = new unsigned char *[rows]; components[1] = bufr = new unsigned char *[rows]; components[2] = bufb = new unsigned char *[rows]; for (i = 0; i < rows; i++){ pg = bufg[i] = new unsigned char[cols*numbytes]; pr = bufr[i] = new unsigned char[cols*numbytes]; pb = bufb[i] = new unsigned char[cols*numbytes]; if (numbytes == 2) { // 2 bytes per pel for (j = 0; j < cols; j++) { *pr++ = (red[i][j] >> 8) & 0xff; *pr++ = red[i][j] & 0xff; *pg++ = (green[i][j] >> 8) & 0xff; *pg++ = green[i][j] & 0xff; *pb++ = (blue[i][j] >> 8) & 0xff; *pb++ = blue[i][j] & 0xff; } } else { for (j = 0; j < cols; j++) { *pr++ = red[i][j]; *pg++ = green[i][j]; *pb++ = blue[i][j]; } } } FILE *cfp = fopen(aptname,"wb"); if (cfp == 0) { cerr << "Cannot open " << aptname << '\n'; return -1; } int retval = apt_encode(components, cfp, rows, cols, 3, quality, "cLiP", numbytes); switch (retval) { case -1: cerr << "APT quality parameter must be between 0 and 100\n"; break; case -2: cerr << "Can't open " << aptname << endl; break; default: // Success break; } for (int compnum = 0; compnum < 3; compnum++) { unsigned char **in = components[compnum]; for (int i = 0; i < rows; i++) delete [] in[i]; delete [] in; } fclose(cfp); return retval; } // Some support functions for the BMP read/write interface namespace clip_internal { #define BYTE_SWAP 1 #ifdef BYTE_SWAP #define READSHORT(s) { unsigned char hi, lo; lo = fin.get(); hi = fin.get(); s = (hi<<8)|lo; } #define WRITESHORT(s) { unsigned char hi=((s)>>8), lo=((s)&0xff); fout << lo << hi; } #define READLONG(s) { unsigned char b1, b2, b3, b4; b1 = fin.get(); b2 = fin.get(); b3 = fin.get(); b4 = fin.get(); (s) = (b4<<24)|(b3<<16)|(b2<<8)|b1; } #define WRITELONG(s) { unsigned char b1((unsigned char)((s)&0xff)), b2((unsigned char)(((s)>>8)&0xff)), b3((unsigned char)(((s)>>16)&0xff)), b4((unsigned char)(((s)>>24)&0xff)); fout << b1 << b2 << b3 << b4; } #else #define READSHORT(s) { unsigned char hi, lo; hi = fin.get(); lo = fin.get(); s = (hi<<8)|lo; } #define WRITESHORT(s) { unsigned char hi=((s)>>8), lo=((s)&0xff); fout << hi << lo; } #define READLONG(s) { unsigned char b1, b2, b3, b4; b4 = fin.get(); b3 = fin.get(); b2 = fin.get(); b1 = fin.get(); (s) = (b4<<24)|(b3<<16)|(b2<<8)|b1; } #define WRITELONG(s) { unsigned char b1((s)&0xff), b2(((s)>>8)&0xff), b3(((s)>>16)&0xff), b4(((s)>>24)&0xff); fout << b4 << b3 << b2 << b1; } #endif void bmp_colour_read(ifstream& fin, const int height, const int width, unsigned char ***components) { int i, j; char c; char *b; char *g; char *r; int padding = (4-((3*width)%4))%4; for (j = 0; j < height; j++) { g = (char *) components[0][height-1-j]; r = (char *) components[1][height-1-j]; b = (char *) components[2][height-1-j]; for (i = 0; i < width; i++) { fin.read(b++, 1); fin.read(g++, 1); fin.read(r++, 1); } for (i = 0; i < padding; i++) fin.read (&c, 1); } } void bmp_mono_read(ifstream& fin, const int height, const int width, unsigned char ***components) { int i, j; char c; char *m; int padding = (4-(width%4))%4; for (j = 0; j < height; j++) { m = (char *) components[0][height-1-j]; for (i = 0; i < width; i++) { fin.read(m++, 1); } for (i = 0; i < padding; i++) fin.read (&c, 1); } } void bmp_colour_write(ofstream& fout, const int height, const int width, unsigned char ***components) { int i, j; char *b; char *g; char *r; int padding = (4-((3*width)%4))%4; for (j = 0; j < height; j++) { g = (char *) components[0][height-1-j]; r = (char *) components[1][height-1-j]; b = (char *) components[2][height-1-j]; for (i = 0; i < width; i++) fout << *b++ << *g++ << *r++; for (i = 0; i < padding; i++) fout << (char) 0; } } void bmp_mono_write(ofstream& fout, const int height, const int width, unsigned char ***components) { int i, j; char *m; int padding = (4-width%4)%4; for (j = 0; j < height; j++) { m = (char *) components[0][height-1-j]; for (i = 0; i < width; i++) fout << *m++; for (i = 0; i < padding; i++) fout << (char) 0; } } int clip_bmp_decode(unsigned char ***components, const char *filename, int& rows, int& cols, int& type) { // Returns number of components or -1 if not a bmp file ifstream fin(filename,ios::binary); if (!fin) return -1; int i, j, idim; char m1, m2; unsigned long filesize, reserved, bitmapoffset, size, width; long height; unsigned short planes, bpp; unsigned long compression, bitmapsize, hres, vres; unsigned long colsused, colsimportant; // Get magic number fin >> m1 >> m2; if ((m1 != 'B')||(m2 != 'M')) { return -1; } READLONG(filesize); READLONG(reserved); READLONG(bitmapoffset); READLONG(size); READLONG(width); READLONG(height); READSHORT(planes); READSHORT(bpp); READLONG(compression); READLONG(bitmapsize); READLONG(hres); READLONG(vres); READLONG(colsused); READLONG(colsimportant); if (!colsused && (bitmapoffset != 54)) colsused = (bitmapoffset - 54)/4; if ((bitmapoffset != 54 + 4*colsused)||(size != 40)||(height < 0)|| (planes != 1)||(compression != 0)) { // Can't handle any of these cases cout << "Sorry I can't handle this kind of .bmp file\n"; return -1; } if (bpp == 24) type = 3; else if ((bpp == 8)&&(colsused)) type = 1; else { cout << "Sorry I can't read .bmp files with fewer than 8 bits per pel\n"; return -1; } rows = height; cols = width; int buffersize = type * cols; buffersize += (4 - ((type * cols) % 4)) % 4; // Padding components[0] = new unsigned char * [rows]; components[0][0] = new unsigned char [rows*cols]; for (i = 1, idim = cols; i < rows; i++, idim += cols) components[0][i] = &components[0][0][idim]; if (type == 1) { unsigned char palette[256][4]; unsigned int pali; for (pali = 0; pali < colsused; pali++) { int val; val = palette[pali][2] = fin.get(); if((palette[pali][0] = fin.get()) != val) type = 3; if((palette[pali][1] = fin.get()) != val) type = 3; palette[pali][3] = fin.get(); } // If all the palette entries are the same, we can keep as // monochrome: if (type == 1) { components[2] = components[1] = components[0]; bmp_mono_read(fin, rows, cols, components); for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) components[0][i][j] = palette[components[0][i][j]][0]; } // Otherwise load as monochrome and then convert else { components[1] = new unsigned char * [rows]; components[1][0] = new unsigned char [rows*cols]; for (i=1, idim = cols; i < rows; i++, idim += cols) components[1][i] = &components[1][0][idim]; components[2] = new unsigned char * [rows]; components[2][0] = new unsigned char [rows*cols]; for (i=1, idim = cols; i < rows; i++, idim += cols) components[2][i] = &components[2][0][idim]; bmp_mono_read(fin, rows, cols, components); for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) { unsigned char index = components[0][i][j]; components[0][i][j] = palette[index][0]; components[1][i][j] = palette[index][1]; components[2][i][j] = palette[index][2]; } } } else { components[1] = new unsigned char * [rows]; components[1][0] = new unsigned char [rows*cols]; for (i=1, idim = cols; i < rows; i++, idim += cols) components[1][i] = &components[1][0][idim]; components[2] = new unsigned char * [rows]; components[2][0] = new unsigned char [rows*cols]; for (i=1, idim = cols; i < rows; i++, idim += cols) components[2][i] = &components[2][0][idim]; bmp_colour_read(fin, rows, cols, components); } return type; } int bmp_write(const char *filename, const int& type, unsigned char ***components, const int& rows, const int& cols) { int i; unsigned long colsused(0); if (type == 1) // Will palettize colsused = 256; ofstream fout(filename,ios::binary); if (!fout) { cerr << "Cannot open " << filename << endl; return -1; } int buffersize = type * cols; buffersize += (4 - ((type * cols)%4)) % 4; // Padding fout << "BM"; // Magic number unsigned long filesize(54 + colsused*4 + buffersize*rows); unsigned long reserved(0), bitmapoffset(54 + colsused*4); unsigned long size(40), width(cols); long height(rows); unsigned short planes(1), bpp(8*type); unsigned long compression(0), bitmapsize(0), hres(0), vres(0); unsigned long colsimportant(0); WRITELONG(filesize); WRITELONG(reserved); WRITELONG(bitmapoffset); WRITELONG(size); WRITELONG(width); WRITELONG(height); WRITESHORT(planes); WRITESHORT(bpp); WRITELONG(compression); WRITELONG(bitmapsize); WRITELONG(hres); WRITELONG(vres); WRITELONG(colsused); WRITELONG(colsimportant); if (type == 1) { unsigned long paletteval = 0x000000; for (i = 0; i < 256; i++) { WRITELONG(paletteval); paletteval += 0x010101; } bmp_mono_write(fout, rows, cols, components); } else bmp_colour_write(fout, rows, cols, components); return 0; } } // clip_internal namespace template int picture_of_::bmpwrite(const char *bmpname) { // Write to bmp format file int rows = rowrange.length(); int cols = colrange.length(); int low, high; int dummyr, dummyc; low = min(dummyr, dummyc); high = max(dummyr, dummyc); if ((low < 0)||(high > 255)) { cerr << "The picture is outside limits [0,255], so"; cerr << " you cannot write to an BMP file.\n)"; cerr << "Use picture_of_::write() not picture_of_::bmpwrite()\n"; return -1; } int i,j; unsigned char **components[3]; unsigned char **buf; unsigned char *pbuf; components[0] = buf = new unsigned char *[rows]; components[1] = buf; components[2] = buf; // So all three components point at same for (i = 0; i < rows; i++) { pbuf = buf[i] = new unsigned char[cols]; for (j = 0; j < cols; j++) *pbuf++ = (unsigned char) pr->buf[i][j]; } int retval = clip_internal::bmp_write(bmpname,1,components,rows,cols); unsigned char **in = components[0]; for (int ii = 0; ii < rows; ii++) delete [] in[ii]; delete [] in; return retval; } template int colour_picture_of_::bmpwrite(const char *bmpname) { // Write to bmp format file if (puremono) { return mono.bmpwrite(bmpname); } int rows = red.rowrange.length(); int cols = red.colrange.length(); int rlow, rhigh; int glow, ghigh; int blow, bhigh; int low, high; int dummyr, dummyc; low = rlow = (int) red.min(dummyr, dummyc); high = rhigh = (int) red.max(dummyr, dummyc); glow = (int) green.min(dummyr, dummyc); ghigh = (int) green.max(dummyr, dummyc); if (glow < low) low = glow; if (ghigh > high) high = ghigh; blow = (int) blue.min(dummyr, dummyc); bhigh = (int) blue.max(dummyr, dummyc); if (blow < low) low = blow; if (bhigh > high) high = bhigh; if ((low < 0)||(high > 255)) { cerr << "The picture is outside limits [0,255], so"; cerr << " you cannot write to an BMP file.\n)"; cerr << "Use colour_picture_of_::write() not colour_picture_of_::bmpwrite()\n"; return -1; } int i,j; unsigned char **components[3]; unsigned char **bufr, **bufg, **bufb; unsigned char *pr, *pg, *pb; components[0] = bufg = new unsigned char *[rows]; components[1] = bufr = new unsigned char *[rows]; components[2] = bufb = new unsigned char *[rows]; for (i = 0; i < rows; i++){ pg = bufg[i] = new unsigned char[cols]; pr = bufr[i] = new unsigned char[cols]; pb = bufb[i] = new unsigned char[cols]; for (j = 0; j < cols; j++) { *pr++ = (unsigned char) red[i][j]; *pg++ = (unsigned char) green[i][j]; *pb++ = (unsigned char) blue[i][j]; } } int retval = clip_internal::bmp_write(bmpname,3,components,rows,cols); for (int compnum = 0; compnum < 3; compnum++) { unsigned char **in = components[compnum]; for (int i = 0; i < rows; i++) delete [] in[i]; delete [] in; } return retval; } // Load from a colour_picture_of_, stacking into a colour format that can // be used by functions like showascolour(). // THIS FUNCTION SHOULDN'T REALLY BE A TEMPLATE BECAUSE IT ONLY MAKES SENSE // WHEN *this IS AN INTEGER PICTURE template int picture_of_::loadascolour(const colour_picture_of_& other) { // Note: don't need to test for puremono in other because in that // case all three of red, green, blue point at mono array and we // just read it three times. int mx = (int) other.max(); int mn = (int) other.min(); if ((mx < 0)||(mx > 255)) { // Got to rerange int oldrange = mx - mn; for (int i = 0; i < nrows(); i++) { PEL *r = other.red.pr->buf[i]; PEL *g = other.green.pr->buf[i]; PEL *b = other.blue.pr->buf[i]; PEL *pel = pr->buf[i]; for (int j = 0; j < ncols(); j++) { int temp; temp = (255*(*r++-mn))/oldrange; temp <<= 8; temp += (255*(*g++-mn))/oldrange; temp <<= 8; temp += (255*(*b++-mn))/oldrange; *pel++ = temp; } } } else { // Can just stack for (int i = 0; i < nrows(); i++) { PEL *r = other.red.pr->buf[i]; PEL *g = other.green.pr->buf[i]; PEL *b = other.blue.pr->buf[i]; PEL *pel = pr->buf[i]; for (int j = 0; j < ncols(); j++) { *pel++ = (((*r++<<8)+*g++)<<8)+*b++; } } } return 1; } /* CLIP_J */ namespace clip_internal { // Font header file: contains 6x13 bitmaps from xterm font starting at '!' static const unsigned char uchar_font[13][659] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, // Following entry changed from 0 to 1 to avoid nasty dots on minus signs 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; } // namespace clip_internal // Construct for textual pictures template picture_of_::picture_of_(const char *text, const int fg, const int bg, const int size) { using namespace clip_internal; const char *p = text; // Work out size required int x = 0; int y = 0; int xmax = 0; while(*p) { if (*p == ' ') { // Write blank } else if (*p == '\t') { // Write blanks until tab stop x += 7; while (x%8) // 7 and 8 have no common factor x += 7; x -=7; // Will be brought back at end of loop } // else if ((*p == '\n')||(*p == '\r')) { else if (*p == '\n'){ if (x > xmax) xmax = x; x = -7; // Will get reset to 0 at end of loop y += 13; } else if ((*p < '!')||(*p > '~')) // chars outside font x -= 7; // Will get brought back at end of loop else { // Write character } p++; x += 7; } if (x > xmax) xmax = x; x = xmax + 1; // To allow 1 pel margin on left y = y + 13 + 1; // To allow 1 pel margin above x *= size; y *= size; init_new(y,x,0);// No border on these pictures for (int i = 0; i < y; i++) for (int j = 0; j < x; j++) pr->buf[i][j] = (PEL) bg; x = size; y = size; // Writing start position is (1,1); p = text; while(*p) { if (*p == ' ') { // Write blank } else if (*p == '\t') { // Write blank int tempx = x/size - 1; tempx += 7; while (tempx%8) tempx += 7; tempx -= 7; x = size*(tempx+1); } // else if ((*p == '\n')||(*p == '\r')) { else if (*p == '\n') { x = -6*size; // Will get reset to 1 at end of loop y += 13*size; } else if ((*p < '!')||(*p > '~')) // chars outside font x -= 7*size; // Will get brought back at end of loop else { int font_offset = 7*(*p - '!') + 1; for (int i = 0; i < size*13; i += size) for (int j = 0; j < size*6; j += size) if (!uchar_font[i/size][font_offset+j/size]){ for (int ii = 0; ii < size; ii++) for (int jj = 0; jj < size; jj++) pr->buf[i+ii+y][j+jj+x] = (PEL) fg; } } p++; x += 7*size; } } /* CLIP_K */ // Main CLIP functionality: pictures, iranges, callbacks. // irange::irange(const int& first, const int& second, const int& last) { if ((end = last) < (start = first)) { cerr << "last < first in irange constructor\n"; cerr << "last = " << last << ", first = "; cerr << first << endl; num_in_irange = 0; spacing = 0; return; } spacing = second - first; if (spacing < 0) { cerr << "Spacing error in irange constructor\n"; num_in_irange = 0; spacing = 0; return; } if (spacing == 0) { spacing = 1; // So that iterations work num_in_irange = 1; } else num_in_irange = (last-first)/spacing + 1; } irange::irange(const int& solevalue) { end = start = solevalue; spacing = 1; // So that iterations work num_in_irange = 1; } irange::irange(const irange& other) { start = other.start; end = other.end; num_in_irange = other.num_in_irange; spacing = other.spacing; } irange::irange() { start = end = 0; spacing = 1; num_in_irange = 0; } irange::~irange() { } int irange::length() { return num_in_irange; } int irange::operator[] (const int& i) const { if (i < 0) return -1; int temp = start + i*spacing; if (temp > end) return -1; return temp; } int irange::operator== (const irange& other) const { if ((other.num_in_irange == num_in_irange)&& (other.start == start) &&(other.end == end)) return 1; return 0; } int irange::operator!= (const irange& other) const { if ((other.num_in_irange == num_in_irange)&& (other.start == start) &&(other.end == end)) return 0; return 1; } irange& irange::operator= (const irange& other) { start = other.start; end = other.end; num_in_irange = other.num_in_irange; spacing = other.spacing; return *this; } irange& irange::operator+= (const int& i) { start += i; end += i; return *this; } irange& irange::operator-= (const int& i) { start -= i; end -= i; return *this; } irange& irange::operator++ () { start++; end++; return *this; } irange& irange::operator-- () { start--; end--; return *this; } irange irange::operator++ (int) { irange temp(*this); start++; end++; return temp; } irange irange::operator-- (int) { irange temp(*this); start--; end--; return temp; } irange operator+ (const irange& o, const int& i) { irange temp(o.istart()+i, o.istart()+o.ispacing()+i, o.iend()+i); return temp; } irange operator+ (const int& i, const irange& o) { irange temp(o.istart()+i, o.istart()+o.ispacing()+i, o.iend()+i); return temp; } irange operator- (const irange& o, const int& i) { irange temp(o.istart()-i, o.istart()+o.ispacing()-i, o.iend()-i); return temp; } irange operator- (const int& i, const irange& o) { irange temp(o.istart()-i, o.istart()+o.ispacing()-i, o.iend()-i); return temp; } template int picture_of_::map(const int myx,const int myy,const picture_of_& from, const int fromx,const int fromy, const int transparency){ // map is function especially for fast texture mapping. // fromx, fromy are ints so are 256 times actual int top = fromy>>8; int left = fromx>>8; PEL *abovep, *belowp, y00, y01, y10, y11; abovep = from.pr->buf[top] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid pr->buf[myy][myx] = y00; return y00; } int a = fromy - (top<<8); int b = fromx - (left<<8); belowp = from.pr->buf[top+1] + left; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) return (pr->buf[myy][myx] = (a*b*(y11 - y01 - y10 + y00) + ((a*(y10 - y00) + b*(y01 - y00))<<8) + (y00<<16))>>16); else { PEL oldpel = pr->buf[myy][myx]; PEL newpel = (a*b*(y11 - y01 - y10 + y00) + ((a*(y10 - y00) + b*(y01 - y00))<<8) + (y00<<16))>>16; return (pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8)); } } template int picture_of_::map(const int myx,const int myy,const picture_of_& from, const double fromx,const double fromy, const int transparency){ // map is function especially for fast texture mapping. int top = (int) fromy; int left = (int) fromx; PEL *abovep, *belowp, y00, y01, y10, y11; abovep = from.pr->buf[top] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid pr->buf[myy][myx] = y00; return y00; } double a = fromy - top; double b = fromx - left; belowp = from.pr->buf[top+1] + left; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) return (pr->buf[myy][myx] = (int)(a*b*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5)); else { PEL oldpel = pr->buf[myy][myx]; PEL newpel = (int)(a*b*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); return (pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8)); } } template void colour_picture_of_::map(const int myx,const int myy,const colour_picture_of_& from, const int fromx,const int fromy, const int transparency){ // map is function especially for fast texture mapping. Avoids having // to work out interpolation values repeatedly // Version with int fromx and fromy has these * 256 int top = fromy>>8; int a = fromy - (top<<8); int left = fromx>>8; int b = fromx - (left<<8); int ab; PEL *abovep, *belowp, y00, y01, y10, y11; if (from.puremono) { abovep = from.mono.pr->buf[top] + left; belowp = from.mono.pr->buf[top+1] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid mono.pr->buf[myy][myx] = y00; return; } y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) mono.pr->buf[myy][myx] = (a*b*(y11 - y01 - y10 + y00) + ((a*(y10 - y00) + b*(y01 - y00))<<8) + (y00<<16))>>16; else { PEL oldpel = mono.pr->buf[myy][myx]; PEL newpel = (a*b*(y11 - y01 - y10 + y00) + ((a*(y10 - y00) + b*(y01 - y00))<<8) + (y00<<16))>>16; mono.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } } else { abovep = from.red.pr->buf[top] + left; belowp = from.red.pr->buf[top+1] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid red.pr->buf[myy][myx] = y00; green.pr->buf[myy][myx] = y00; blue.pr->buf[myy][myx] = y00; return; } y01 = *abovep; y10 = *belowp++; y11 = *belowp; ab = a*b; a <<= 8; b <<= 8; if (!transparency) red.pr->buf[myy][myx] = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; else { PEL oldpel = red.pr->buf[myy][myx]; PEL newpel = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; red.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } abovep = from.green.pr->buf[top] + left; belowp = from.green.pr->buf[top+1] + left; y00 = *abovep++; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) green.pr->buf[myy][myx] = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; else { PEL oldpel = green.pr->buf[myy][myx]; PEL newpel = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; green.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } abovep = from.blue.pr->buf[top] + left; belowp = from.blue.pr->buf[top+1] + left; y00 = *abovep++; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) blue.pr->buf[myy][myx] = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; else { PEL oldpel = blue.pr->buf[myy][myx]; PEL newpel = (ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + (y00<<16))>>16; blue.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } } } template void colour_picture_of_::map(const int myx,const int myy,const colour_picture_of_& from, const double fromx,const double fromy, const int transparency){ // map is function especially for fast texture mapping. Avoids having // to work out interpolation values repeatedly int top = (int) fromy; double a = fromy - top; int left = (int) fromx; double b = fromx - left; double ab; PEL *abovep, *belowp, y00, y01, y10, y11; if (from.puremono) { abovep = from.mono.pr->buf[top] + left; belowp = from.mono.pr->buf[top+1] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid mono.pr->buf[myy][myx] = y00; return; } y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) mono.pr->buf[myy][myx] = (int)(a*b*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); else { PEL oldpel = mono.pr->buf[myy][myx]; PEL newpel = (int)(a*b*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); mono.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } } else { abovep = from.red.pr->buf[top] + left; belowp = from.red.pr->buf[top+1] + left; y00 = *abovep++; if (y00 < -255){ // Flag for invalid red.pr->buf[myy][myx] = y00; green.pr->buf[myy][myx] = y00; blue.pr->buf[myy][myx] = y00; return; } y01 = *abovep; y10 = *belowp++; y11 = *belowp; ab = a*b; if (!transparency) red.pr->buf[myy][myx] = (int)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); else { PEL oldpel = red.pr->buf[myy][myx]; PEL newpel = (int)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); red.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } abovep = from.green.pr->buf[top] + left; belowp = from.green.pr->buf[top+1] + left; y00 = *abovep++; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) green.pr->buf[myy][myx] = (int)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); else { PEL oldpel = green.pr->buf[myy][myx]; PEL newpel = (PEL)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); green.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } abovep = from.blue.pr->buf[top] + left; belowp = from.blue.pr->buf[top+1] + left; y00 = *abovep++; y01 = *abovep; y10 = *belowp++; y11 = *belowp; if (!transparency) blue.pr->buf[myy][myx] = (PEL)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); else { PEL oldpel = blue.pr->buf[myy][myx]; PEL newpel = (PEL)(ab*(y11 - y01 - y10 + y00) + a*(y10 - y00) + b*(y01 - y00) + y00 + 0.5); blue.pr->buf[myy][myx] = ((transparency*oldpel + (256-transparency)*newpel)>>8); } } } namespace clip_internal { inline int gr_rescale(int in, int inlow, int inhigh, int outlow, int outhigh) { return((in - inlow)*outhigh + (inhigh - in)*outlow)/(inhigh - inlow); } } // namespace clip_internal template int picture_of_::draw_graph(const int *yvals, const int numvals, const PEL drawshade, int minval, int maxval) { using namespace clip_internal; const int *p = yvals; int i; if (minval == -1) { minval = *p; maxval = *p++; for (i = 1; i < numvals; i++) { if (*p < minval) minval = *p; if (*p > maxval) maxval = *p; p++; } } if (minval == maxval) maxval = minval + 1; // Avoid divide by zero p = yvals; int *disp_xvals = new int[numvals]; int *disp_yvals = new int[numvals]; for (i = 0; i < numvals; i++) { disp_xvals[i] = gr_rescale(i,0,numvals-1,0,ncols()-1); disp_yvals[i] = gr_rescale(*p++,minval, maxval,nrows()-1,0); } for (i = 1; i < numvals; i++) { draw_line(disp_xvals[i-1],disp_yvals[i-1], disp_xvals[i],disp_yvals[i],drawshade); } delete [] disp_xvals; delete [] disp_yvals; return 1; } template int picture_of_::wait(const int milliseconds) { return clip_internal::cimg_wait(milliseconds); } template int colour_picture_of_::wait(const int milliseconds) { return clip_internal::cimg_wait(milliseconds); } /* CLIP_L */ // Matrix functions that do not use others' adapted code are defined at the // top of the file. namespace clip_internal { // This section includes functions the Bioinformatics Template Library (BTL) // and from Brent Worden's numerics library. The BTL uses the GPL and // Brian Worden allows free use. // This code is part of the Bioinformatics Template Library (BTL). // // Copyright (C) 1997,1998 Birkbeck College, Malet Street, London WC1E 7HX, U.K. // (classlib@mail.cryst.bbk.ac.uk) // //............................................................................. // Subroutine to compute eigenvalues & eigenvectors of a real symmetric // matrix, from IBM SSP manual (see p165). // // Ian Tickle April 1992 // // (modified by David Moss February 1993) // // Eigenvalues & vectors of real symmetric matrix stored in triangular form. // // Arguments: // // mv = 0 to compute eigenvalues only. // mv = 1 to compute eigenvalues & eigenvectors. // // n - supplied dimension of n*n matrix. // // a - supplied array of size n*(n+1)/2 containing n*n matrix in the order: // // 1 2 3 ... // 1 a[0] // 2 a[1] a[2] // 3 a[3] a[4] a[5] ... // ... // NOTE a is used as working space and is overwritten on return. // Eigenvalues are written into diagonal elements of a // i.e. a[0] a[2] a[5] for a 3*3 matrix. // // r - Resultant matrix of eigenvectors stored columnwise in the same // order as eigenvalues. template void BTL_Eigen(bool mv, int n, T* a, T* r) { int i, il, ilq, ilr, im, imq, imr, ind, iq, j, l, ll, lm, lq, m, mm, mq; T anorm, anrmx, cosx, cosx2, sincs, sinx, sinx2, thr, x, y; T theshold = DBL_EPSILON; if (mv) { for (i=1; i0.) { anorm=sqrt(2.*anorm); anrmx=theshold*anorm/n; /* Compute threshold and initialise flag. */ thr=anorm; do { thr/=n; do { ind=0; l=0; do { lq=l*(l+1)/2; ll=l+lq; m=l+1; ilq=n*l; do /* Compute sin & cos. */ { mq=m*(m+1)/2; lm=l+mq; if (fabs(a[lm])>=thr) { ind=1; mm=m+mq; x=.5*(a[ll]-a[mm]); y=-a[lm]/sqrt(a[lm]*a[lm]+x*x); if (x<0.) y=-y; sinx=y/sqrt(2.*(1.+(sqrt(1.-y*y)))); sinx2=sinx*sinx; cosx=sqrt(1.-sinx2); cosx2=cosx*cosx; sincs=sinx*cosx; /* Rotate l & m columns. */ imq=n*m; for (i=0; ianrmx); } } // Sort eigenvalues & eigenvectors (if mv=1) in order of descending eigenvalue. // Arguments are as for eigen, which must have been called. template void BTL_Esort(bool mv, int n, T* a, T* r) { int i, im, j, k, km, l, m; T am; k=0; for (i=0; ii && a[l]>am) { im=j; km=l; am=a[l]; } l+=j+2; } if (im!=i) { a[km]=a[k]; a[k]=am; if (mv) { l=n*i; m=n*im; for (j=0; j inline T Worden_absoluteValue(T value) //-------------------------------------------------------------------- // Return the absolute value of value. //-------------------------------------------------------------------- { static T zero(0); return value < zero ? -value : value; }; template T Worden_determinant(picture_of_& a) //------------------------------------------------------------------- // Returns the determinant of the matrix a. //------------------------------------------------------------------- { picture_of_ cp(a); T ret; int i; int n = cp.nrows(); int *indx = new int[n]; int sgn; if(Worden_ludcmp(cp, indx, sgn)){ ret = T(sgn); for(i = 0; i < n; i++){ ret *= cp[i][i]; } } else { ret = 0.0; } delete [] indx; return ret; }; template bool Worden_inverse(picture_of_& a) //------------------------------------------------------------------- // Replaces the matrix a with its inverse. The routine returns true // if the inversion was successful, otherwise it returns false. //------------------------------------------------------------------- { int i, j; bool ret = false; int n = a.nrows(); picture_of_ ai(n, n); int *indx = new int[n]; T *col = new T[n]; int d; if(Worden_ludcmp(a, indx, d)){ for(j = 0; j < n; j++){ for(i = 0; i < n; i++){ col[i] = T(0); } col[j] = T(1); Worden_lubksb(a, indx, col); for(i = 0; i < n; i++){ ai[i][j] = col[i]; } } a = ai; ret = true; } delete [] indx; delete [] col; return ret; }; template void Worden_lubksb(picture_of_& a, int *indx, T *b) //------------------------------------------------------------------- // Given a matrix a and permutation vector indx returned from ludcmp, // this routines solves the set of linear equations a.x = b. On // input b holds the right-hand side vector. On output, it holds the // solution vector x. a and indx are not modified by this routine // and can be left in place for successive colls with different // right-hand sides b. //------------------------------------------------------------------- { int i, ii = -1, ip, j; T sum; int n = a.nrows(); for(i = 0; i < n; i++){ ip = indx[i]; sum = b[ip]; b[ip] = b[i]; if(ii > -1){ for(j = ii; j <= i - 1; j++){ sum -= a[i][j] * b[j]; } } else if(sum){ ii = i; } b[i] = sum; } for(i = n - 1; i >= 0; i--){ sum = b[i]; for(j = i + 1; j < n; j++){ sum -= a[i][j] * b[j]; } b[i] = sum / a[i][i]; } }; template bool Worden_ludcmp(picture_of_& a, int *indx, int& d) //------------------------------------------------------------------- // See above comment. d is output as 1 or -1 depending on whether // the number of row interchanges was even or odd, respectively. // This routine is used in combination with lubksb to solve linear // equations or invert a matrix. //------------------------------------------------------------------- { int i, imax, j, k; T big, dum, sum, temp; int n = a.nrows(); T *vv = new T[n]; T zero(0); T one(1); d = 1; for(i = 0; i < n; i++){ big = zero; for(j = 0; j < n; j++){ if((temp = Worden_absoluteValue(a[i][j])) > big){ big = temp; } } if(big == zero){ delete [] vv; cout << "Can't invert matrix in ludcmp: matrix singular\n"; return false; } vv[i] = one / big; } for(j = 0; j < n; j++){ for(i = 0; i < j; i++){ sum = a[i][j]; for(k = 0; k < i; k++){ sum -= a[i][k] * a[k][j]; } a[i][j] = sum; } big = zero; for(i = j; i < n; i++){ sum = a[i][j]; for(k = 0; k < j; k++){ sum -= a[i][k] * a[k][j]; } a[i][j] = sum; if((dum = vv[i] * Worden_absoluteValue(sum)) >= big){ big = dum; imax = i; } } if(j != imax){ for(k = 0; k < n; k++){ dum = a[imax][k]; a[imax][k] = a[j][k]; a[j][k] = dum; } d = -d; vv[imax] = vv[j]; } indx[j] = imax; if(a[j][j] == 0.0){ a[j][j] = NUMERICS_FLOAT_MIN; } if(j != n - 1){ dum = one / (a[j][j]); for(i = j + 1; i < n; i++){ a[i][j] *= dum; } } } delete [] vv; return true; }; template bool Worden_linsolve(const picture_of_& a, picture_of_& b, picture_of_& x) //------------------------------------------------------------------- // Solves the set of linear equations a.x = b. The routine returns // true if the solution vector is successfully found, otherwise it // returns false. //------------------------------------------------------------------- { picture_of_ ac(a); picture_of_ bc(b); int n = a.nrows(); bool ret = false; int d; int *indx = new int[n]; if(Worden_ludcmp(ac, indx, d)){ Worden_lubksb(ac, indx, bc[0]); x = bc; ret = true; } delete [] indx; return ret; }; template bool Worden_eigsolve(const picture_of_& a, const T& evalue, picture_of_& evec) // // Adapted version of linsolve that can be used when you have an eigenvalue // and want to calculate the corresponding eigenvector. // { int redrows = a.nrows()-1; picture_of_ ac(redrows,redrows); picture_of_ bvec(1,a.nrows()); // Although only redrows used, // have to do this so we don't // get a warning in linsolve irange lessone(0,1,redrows-1); irange last(redrows,redrows,redrows); irange first(0,0,0); ac = a[lessone][lessone]; bvec[first][lessone] = a[last][lessone]; bvec *= -1; int i; for (i = 0; i < redrows; i++) ac[i][i] -= evalue; bool retval = Worden_linsolve(ac, bvec, evec); // Normalize the evec; evec[0][redrows] = 1; T total = 0; for (i = 0; i < a.nrows(); i++) total += evec[0][i]*evec[0][i]; total = sqrt(total); for (i = 0; i < a.nrows(); i++) evec[0][i] /= total; return retval; } }; // namespace clip_internal // // End of adapted code. // Following sections provide CLIP interface // template int picture_of_::mx_eigensystem(picture_of_& in, picture_of_& evec) { // in is a real symmetric matrix // eigenvalues are put into the first row of this picture // evec is a matrix to store the evectors int n = in.nrows(); if (n != in.ncols()) { cout << "Can't do eigenanalysis of a non-square matrix\n"; return -1; } if ((n != evec.ncols())||(n != evec.nrows())) { cout << "Eigenvector matrix is not same size as input mx\n"; return -1; } if (n != ncols()) { cout << "Eigenvalue cols is not same as input mx size\n"; return -1; } // Create working space T *inmx = new T[((n+1)*n)/2]; // Triangular // To avoid using lots of memory, we'll write the evecs into the // evec picture, but afterwards we'll have to readjust them for // the CLIP picture borders. // I.e. we don't use // T *outmx = new T[n*n]; // but rather T *outmx = evec[n-1] + n; // which makes outmx point at the end of the picture (even if it // has no border), then backstep by the size of the 1D array that // BTL_Eigen and BTL_Esort expect outmx -= n*n; int mx_i; int pic_i, pic_j; int i; // Put elements from input picture into inmx pic_i = pic_j = 0; mx_i = 0; while (pic_i < n) { inmx[mx_i++] = in[pic_i][pic_j++]; if (pic_j > pic_i) { pic_i++; pic_j = 0; } } clip_internal::BTL_Eigen(1, n, inmx, outmx); clip_internal::BTL_Esort(1, n, inmx, outmx); // Copy out evals int diaginc = 2; for (i = 0, mx_i = 0; i < n; i++, mx_i += diaginc++) pr->buf[0][i] = inmx[mx_i]; // Copy out evecs // Actually following code just shifts the evec values inside // the evec picture. mx_i = 0; for (pic_i = 0; pic_i < n; pic_i++) for (pic_j = 0; pic_j < n; pic_j++) evec[pic_i][pic_j] = outmx[mx_i++]; delete [] inmx; // delete [] outmx; return 0; } template int picture_of_::mx_eigenvalues(picture_of_& in) { // in is a real symmetric matrix // evals are stored in first row of this picture int n = in.nrows(); if (n != in.ncols()) { cout << "Can't do eigenanalysis of a non-square matrix\n"; return -1; } if (n != ncols()) { cout << "Eigenvalue cols is not same as input mx size\n"; return -1; } // Create working space T *inmx = new T[((n+1)*n)/2]; // Triangular int mx_i; int pic_i, pic_j; int i; // Put elements from input picture into inmx pic_i = pic_j = 0; mx_i = 0; while (pic_i < n) { inmx[mx_i++] = in[pic_i][pic_j++]; if (pic_j > pic_i) { pic_i++; pic_j = 0; } } clip_internal::BTL_Eigen(0, n, inmx, inmx); clip_internal::BTL_Esort(0, n, inmx, inmx); // Copy out evals int diaginc = 2; for (i = 0, mx_i = 0; i < n; i++, mx_i += diaginc++) pr->buf[0][i] = inmx[mx_i]; delete [] inmx; return 0; } template int picture_of_::mx_inverse(picture_of_& A) { *this = A; return clip_internal::Worden_inverse(*this); } template int picture_of_::mx_eigenvector(picture_of_& A, PEL evalue) { return clip_internal::Worden_eigsolve(A, evalue, *this); } template int picture_of_::mx_solve(picture_of_& transform, picture_of_& b) { return clip_internal::Worden_linsolve(transform, b, *this); } template PEL picture_of_::mx_determinant() { return clip_internal::Worden_determinant(*this); } } // End of CLIP namespace // Final bit of doxygen stuff for the main page /** \mainpage \section introduction Reference Documentation CLIP is a C++ Library for Image Processing. These are its reference pages generated from the single CLIP source file "picture.h" by doxygen. Navigate through the documentation using the menu above. For introduction, examples and tutorials please see http://www.intuac.com/userport/john/clip104.html */ using namespace clip; #define picture_of_int picture_of_ #define colour_picture colour_picture_of_ #define parampic parampic_of_ #endif