// Copyright (C) 1999-2018 // Smithsonian Astrophysical Observatory, Cambridge, MA, USA // For conditions of distribution and use, see copyright notice in "copyright" #include #include "frame.h" #include "fitsimage.h" #include "ps.h" #include "analysis.h" #include "sigbus.h" // Frame Member Functions Frame::Frame(Tcl_Interp* i, Tk_Canvas c, Tk_Item* item) : FrameBase(i,c,item) { context = new Context(); context->parent(this); currentContext = context; keyContext = context; colormapData =NULL; cmapID = 1; bias = 0.5; contrast = 1.0; colorCount = 0; colorScale = NULL; colorCells = NULL; maskColorName = dupstr("red"); maskAlpha = 1; maskMark = FitsMask::NONZERO; maskLow = 0; maskHigh = 0; maskSystem = Coord::PHYSICAL; } Frame::~Frame() { if (context) delete context; if (colorScale) delete colorScale; if (colorCells) delete [] colorCells; if (colormapData) delete [] colormapData; if (maskColorName) delete [] maskColorName; } void Frame::alignWCS() { Base::alignWCS(); updateMaskMatrices(); } unsigned char* Frame::blend(unsigned char* src, unsigned char* msk, int width, int height) { unsigned char* sptr = src; // 3 component unsigned char* mptr = msk; // 4 component, premultiplied for (int jj=0; jjred; *ptr++ = (unsigned char)bgColor->green; *ptr++ = (unsigned char)bgColor->blue; } } if (!context->cfits) return img; // basics int length = colorScale->size() - 1; const unsigned char* table = colorScale->psColors(); FitsImage* sptr = context->cfits; int mosaic = isMosaic(); // variable double* mm = sptr->matrixToData(sys).mm(); FitsBound* params = sptr->getDataParams(context->secMode()); int srcw = sptr->width(); double ll = sptr->low(); double hh = sptr->high(); double diff = hh - ll; // main loop unsigned char* dest = img; SETSIGBUS for (long jj=0; jjcfits; mm = sptr->matrixToData(sys).mm(); params = sptr->getDataParams(context->secMode()); srcw = sptr->width(); ll = sptr->low(); hh = sptr->high(); diff = hh - ll; } do { double xx = ii*mm[0] + jj*mm[3] + mm[6]; double yy = ii*mm[1] + jj*mm[4] + mm[7]; if (xx>=params->xmin && xxxmax && yy>=params->ymin && yyymax) { double value = sptr->getValueDouble(long(yy)*srcw + long(xx)); if (isfinite(diff) && isfinite(value)) { if (value <= ll) { *(dest+2) = table[0]; *(dest+1) = table[1]; *dest = table[2]; } else if (value >= hh) { *(dest+2) = table[length*3]; *(dest+1) = table[length*3+1]; *dest = table[length*3+2]; } else { int l = (int)(((value - ll)/diff * length) + .5); *(dest+2) = table[l*3]; *(dest+1) = table[l*3+1]; *dest = table[l*3+2]; } } else { *(dest+2) = nanColor->blue; *(dest+1) = nanColor->green; *dest = nanColor->red; } break; } else { if (mosaic) { sptr = sptr->nextMosaic(); if (sptr) { mm = sptr->matrixToData(sys).mm(); params = sptr->getDataParams(context->secMode()); srcw = sptr->width(); ll = sptr->low(); hh = sptr->high(); diff = hh - ll; } } } } while (mosaic && sptr); } } CLEARSIGBUS if (img) { FitsMask* mptr = mask.tail(); while (mptr) { unsigned char* msk = fillMask(mptr, width, height, sys); blend(img,msk,width,height); delete [] msk; mptr = mptr->previous(); } } return img; } unsigned char* Frame::fillMask(FitsMask* msk, int width, int height, Coord::InternalSystem sys) { // img unsigned char* img = new unsigned char[width*height*4]; memset(img,0,width*height*4); Context* cc = msk->context(); FitsImage* currentMsk = cc->fits; XColor* maskColor = msk->color(); FitsMask::MaskType mark = msk->mark(); double low = msk->low(); double high = msk->high(); if (!currentMsk) return img; // basics FitsImage* sptr = currentMsk; int mosaic = cc->isMosaic(); // variable double* mm = sptr->matrixToData(sys).mm(); FitsBound* params = sptr->getDataParams(cc->secMode()); int srcw = sptr->width(); // main loop unsigned char* dest = img; SETSIGBUS for (long jj=0; jjmatrixToData(sys).mm(); params = sptr->getDataParams(cc->secMode()); srcw = sptr->width(); } do { double xx = ii*mm[0] + jj*mm[3] + mm[6]; double yy = ii*mm[1] + jj*mm[4] + mm[7]; if (xx>=params->xmin && xxxmax && yy>=params->ymin && yyymax) { float value = sptr->getValueFloat(long(yy)*srcw + long(xx)); switch (mark) { case FitsMask::ZERO: if (value==0) { *dest = ((unsigned char)maskColor->red)*maskAlpha; *(dest+1) = ((unsigned char)maskColor->green)*maskAlpha; *(dest+2) = ((unsigned char)maskColor->blue)*maskAlpha; *(dest+3) = 1; } break; case FitsMask::NONZERO: if (value!=0) { *dest = ((unsigned char)maskColor->red)*maskAlpha; *(dest+1) = ((unsigned char)maskColor->green)*maskAlpha; *(dest+2) = ((unsigned char)maskColor->blue)*maskAlpha; *(dest+3) = 1; } break; case FitsMask::NaN: if (isnan(value) || isinf(value)) { *dest = ((unsigned char)maskColor->red)*maskAlpha; *(dest+1) = ((unsigned char)maskColor->green)*maskAlpha; *(dest+2) = ((unsigned char)maskColor->blue)*maskAlpha; *(dest+3) = 1; } break; case FitsMask::NONNaN: if (!isnan(value) && !isinf(value)) { *dest = ((unsigned char)maskColor->red)*maskAlpha; *(dest+1) = ((unsigned char)maskColor->green)*maskAlpha; *(dest+2) = ((unsigned char)maskColor->blue)*maskAlpha; *(dest+3) = 1; } break; case FitsMask::RANGE: if (value>=low && value<=high) { *dest = ((unsigned char)maskColor->red)*maskAlpha; *(dest+1) = ((unsigned char)maskColor->green)*maskAlpha; *(dest+2) = ((unsigned char)maskColor->blue)*maskAlpha; *(dest+3) = 1; } break; } break; } else { if (mosaic) { sptr = sptr->nextMosaic(); if (sptr) { mm = sptr->matrixToData(sys).mm(); params = sptr->getDataParams(cc->secMode()); srcw = sptr->width(); } } } } while (mosaic && sptr); } } CLEARSIGBUS return img; } int Frame::isIIS() { return context->cfits && context->cfits->isIIS(); } void Frame::pushMatrices() { // alway identity Matrix rgbToRef; Base::pushMatrices(keyContext->fits, rgbToRef); // now any masks FitsMask* msk = mask.tail(); while (msk) { Base::pushMatrices(msk->context()->fits, msk->mm()); msk = msk->previous(); } } void Frame::pushMagnifierMatrices() { Base::pushMagnifierMatrices(keyContext->fits); // now any masks FitsMask* msk = mask.tail(); while (msk) { Base::pushMagnifierMatrices(msk->context()->fits); msk = msk->previous(); } } void Frame::pushPannerMatrices() { Base::pushPannerMatrices(keyContext->fits); // now any masks FitsMask* msk = mask.tail(); while (msk) { Base::pushPannerMatrices(msk->context()->fits); msk = msk->previous(); } } void Frame::pushPSMatrices(float scale, int width, int height) { Base::pushPSMatrices(keyContext->fits, scale, width, height); // now any masks FitsMask* msk = mask.tail(); while (msk) { Base::pushPSMatrices(msk->context()->fits, scale, width, height); msk = msk->previous(); } } void Frame::reset() { cmapID = 1; bias = 0.5; contrast = 1.0; context->resetSecMode(); context->updateClip(); Base::reset(); } void Frame::updateColorCells(unsigned char* cells, int cnt) { colorCount = cnt; if (colorCells) delete [] colorCells; colorCells = new unsigned char[cnt*3]; if (!colorCells) { internalError("Unable to Alloc colorCells"); return; } memcpy(colorCells, cells, cnt*3); } void Frame::updateMaskMatrices() { // image,pysical,amplifier,detector are ok, check for wcs if (maskSystem >= Coord::WCS) { FitsMask* mptr = mask.head(); while (mptr) { if (mptr->context()->fits && !mptr->context()->fits->hasWCS(maskSystem)) { maskSystem = Coord::IMAGE; break; } mptr = mptr->next(); } } // mask align FitsMask* mptr = mask.head(); while (mptr) { mptr->mm().identity(); if (mptr->context()->fits && keyContext->fits) { switch (maskSystem) { case Coord::IMAGE: // nothing to do here break; case Coord::PHYSICAL: mptr->mm() = mptr->context()->fits->imageToPhysical * keyContext->fits->physicalToImage; break; case Coord::AMPLIFIER: mptr->mm() = mptr->context()->fits->imageToAmplifier * keyContext->fits->amplifierToImage; break; case Coord::DETECTOR: mptr->mm() = mptr->context()->fits->imageToDetector * keyContext->fits->detectorToImage; break; default: mptr->mm() = calcAlignWCS(keyContext->fits, mptr->context()->fits, maskSystem, maskSystem, Coord::FK5); break; } } mptr = mptr->next(); } } void Frame::unloadFits() { if (DebugPerf) cerr << "Frame::unloadFits()" << endl; // clean up from iis if needed if (isIIS()) context->resetIIS(); context->unload(); // delete any masks mask.deleteAll(); Base::unloadFits(); } // Commands void Frame::getMaskColorCmd() { Tcl_AppendResult(interp, maskColorName, NULL); } void Frame::getMaskMarkCmd() { switch (maskMark) { case FitsMask::ZERO: Tcl_AppendResult(interp, "zero", NULL); break; case FitsMask::NONZERO: Tcl_AppendResult(interp, "nonzero", NULL); break; case FitsMask::NaN: Tcl_AppendResult(interp, "nan", NULL); break; case FitsMask::NONNaN: Tcl_AppendResult(interp, "nonnan", NULL); break; case FitsMask::RANGE: Tcl_AppendResult(interp, "range", NULL); break; } } void Frame::getMaskRangeCmd() { ostringstream str; str << maskLow << ' ' << maskHigh << ends; Tcl_AppendResult(interp, str.str().c_str(), NULL); } void Frame::getMaskSystemCmd() { printCoordSystem(maskSystem); } void Frame::getMaskTransparencyCmd() { printDouble((1-maskAlpha)*100.); } void Frame::maskClearCmd() { mask.deleteAll(); update(BASE); } void Frame::maskColorCmd(const char* color) { if (maskColorName) delete [] maskColorName; maskColorName = dupstr(color); } void Frame::maskSystemCmd(Coord::CoordSystem sys) { maskSystem = sys; } void Frame::maskTransparencyCmd(float tt) { maskAlpha = 1-(tt/100.); update(BASE); } void Frame::colormapCmd(int id, float b, float c, int i, unsigned char* cells, int cnt) { cmapID = id; bias = b; contrast = c; invert = i; updateColorCells(cells, cnt); updateColorScale(); update(BASE); } void Frame::colormapBeginCmd() { // we need a colorScale before we can render if (!validColorScale()) return; // we need some fits data // we assume the colorScale length will not change during motion calls if (!context->cfits) return; int width = options->width; int height = options->height; // Create XImage if (!(colormapXM = XGetImage(display, pixmap, 0, 0, width, height, AllPlanes, ZPixmap))) { internalError("Unable to Create Colormap XImage"); return; } // Create Pixmap colormapPM = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height, depth); if (!colormapPM) { internalError("Unable to Create Colormap Pixmap"); return; } // colormapGCXOR colormapGCXOR = XCreateGC(display, Tk_WindowId(tkwin), 0, NULL); // Create table index array if (colormapData) delete [] colormapData; colormapData = new long[width*height]; if (!colormapData) { internalError("Unable to alloc tmp data array"); return; } // fill data array // basics int bytesPerPixel = colormapXM->bits_per_pixel/8; int length = colorScale->size() - 1; int last = length * bytesPerPixel; FitsImage* sptr = context->cfits; int mosaic = isMosaic(); long* dest = colormapData; // variable double* mm = sptr->matrixToData(Coord::WIDGET).mm(); FitsBound* params = sptr->getDataParams(context->secMode()); int srcw = sptr->width(); double ll = sptr->low(); double hh = sptr->high(); double diff = hh - ll; // main loop SETSIGBUS for (long jj=0; jjcfits; mm = sptr->matrixToData(Coord::WIDGET).mm(); params = sptr->getDataParams(context->secMode()); srcw = sptr->width(); ll = sptr->low(); hh = sptr->high(); diff = hh - ll; } do { double xx = ii*mm[0] + jj*mm[3] + mm[6]; double yy = ii*mm[1] + jj*mm[4] + mm[7]; if (xx>=params->xmin && xxxmax && yy>=params->ymin && yyymax) { double value = sptr->getValueDouble(long(yy)*srcw + long(xx)); if (isfinite(diff) && isfinite(value)) { if (value <= ll) *dest = 0; else if (value >= hh) *dest = last; else *dest = (int)(((value - ll)/diff * length) + .5)*bytesPerPixel; } else *dest = -1; break; } else { if (mosaic) { sptr = sptr->nextMosaic(); if (sptr) { mm = sptr->matrixToData(Coord::WIDGET).mm(); params = sptr->getDataParams(context->secMode()); srcw = sptr->width(); ll = sptr->low(); hh = sptr->high(); diff = hh - ll; } } } } while (mosaic && sptr); } } CLEARSIGBUS } void Frame::colormapMotionCmd(int id, float b, float c, int i, unsigned char* cells, int cnt) { // we need a colorScale before we can render if (!validColorScale()) return; // first check for change if (cmapID == id && bias == b && contrast == c && invert == i && colorCells) return; // we got a change cmapID = id; bias = b; contrast = c; invert = i; updateColorCells(cells, cnt); updateColorScale(); // if we have no data, stop now if (!context->cfits) return; // clear ximage int& width = colormapXM->width; int& height = colormapXM->height; char* data = colormapXM->data; int bytesPerPixel = colormapXM->bits_per_pixel/8; int& bytesPerLine = colormapXM->bytes_per_line; const unsigned char* table = colorScale->colors(); long* src = colormapData; for (long jj=0; jjsetIIS(); FitsImage* img = new FitsImageIIS(currentContext, interp, width, height); loadDone(currentContext->load(ALLOC, "", img)); } void Frame::iisEraseCmd() { if (context->cfits) ((FitsImageIIS*)context->cfits)->iisErase(); } void Frame::iisGetCmd(char* dest, int xx, int yy, int dx, int dy) { if (context->cfits) { char* buf = ((FitsImageIIS*)context->cfits)->iisGet(xx,yy,dx,dy); memcpy(dest, buf, dx*dy); delete [] buf; } } void Frame::iisSetCmd(const char* src, int xx, int yy, int dx, int dy) { if (context->cfits) ((FitsImageIIS*)context->cfits)->iisSet(src, xx, yy, dx, dy); } void Frame::iisWCSCmd(const Matrix& mx, const Vector& z, int zt) { if (context->cfits) ((FitsImageIIS*)context->cfits)->iisWCS(mx, z, zt); } void Frame::savePhotoCmd(const char* ph) { FitsImage* fits = currentContext->cfits; if (!fits) return; // basics int length = colorScale->size() - 1; const unsigned char* table = colorScale->psColors(); // variable FitsBound* params = fits->getDataParams(context->secMode()); double ll = fits->low(); double hh = fits->high(); double diff = hh - ll; int width = params->xmax - params->xmin; int height = params->ymax - params->ymin; // photo if (*ph == '\0') { Tcl_AppendResult(interp, "bad image name ", NULL); return; } Tk_PhotoHandle photo = Tk_FindPhoto(interp, ph); if (!photo) { Tcl_AppendResult(interp, "bad image handle ", NULL); return; } if (Tk_PhotoSetSize(interp, photo, width, height) != TCL_OK) { Tcl_AppendResult(interp, "bad photo set size ", NULL); return; } Tk_PhotoBlank(photo); Tk_PhotoImageBlock block; if (!Tk_PhotoGetImage(photo,&block)) { Tcl_AppendResult(interp, "bad image block ", NULL); return; } if (block.pixelSize<4) { Tcl_AppendResult(interp, "bad pixel size ", NULL); return; } // main loop SETSIGBUS unsigned char* dest = block.pixelPtr; for (long jj=params->ymax-1; jj>=params->ymin; jj--) { for (long ii=params->xmin; iixmax; ii++, dest += block.pixelSize) { double value = fits->getValueDouble(Vector(ii,jj)); if (isfinite(diff) && isfinite(value)) { if (value <= ll) { *(dest+block.offset[0]) = table[2]; *(dest+block.offset[1]) = table[1]; *(dest+block.offset[2]) = table[0]; *(dest+block.offset[3]) = 255; } else if (value >= hh) { *(dest+block.offset[0]) = table[length*3+2]; *(dest+block.offset[1]) = table[length*3+1]; *(dest+block.offset[2]) = table[length*3]; *(dest+block.offset[3]) = 255; } else { int l = (int)(((value - ll)/diff * length) + .5); *(dest+block.offset[0]) = table[l*3+2]; *(dest+block.offset[1]) = table[l*3+1]; *(dest+block.offset[2]) = table[l*3]; *(dest+block.offset[3]) = 255; } } else { *(dest+block.offset[0]) = nanColor->red; *(dest+block.offset[1]) = nanColor->green; *(dest+block.offset[2]) = nanColor->blue; *(dest+block.offset[3]) = 255; } } } CLEARSIGBUS if (Tk_PhotoPutBlock(interp, photo, &block, 0, 0, width, height, TK_PHOTO_COMPOSITE_SET) != TCL_OK) { Tcl_AppendResult(interp, "bad put block ", NULL); return; } }