// adapted from http://cpansearch.perl.org/src/RJRAY/Image-Size-3.230/lib/Image/Size.pm Size BitmapSizeFromData(const char *data, size_t len) { Size result; ByteReader r(data, len); switch (GfxFormatFromData(data, len)) { case Img_BMP: if (len >= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)) { BITMAPINFOHEADER bmi; bool ok = r.UnpackLE(&bmi, sizeof(bmi), "3d2w6d", sizeof(BITMAPFILEHEADER)); CrashIf(!ok); result.Width = bmi.biWidth; result.Height = bmi.biHeight; } break; case Img_GIF: if (len >= 13) { // find the first image's actual size instead of using the // "logical screen" size which is sometimes too large size_t ix = 13; // skip the global color table if ((r.Byte(10) & 0x80)) ix += 3 * (1 << ((r.Byte(10) & 0x07) + 1)); while (ix + 8 < len) { if (r.Byte(ix) == 0x2C) { result.Width = r.WordLE(ix + 5); result.Height = r.WordLE(ix + 7); break; } else if (r.Byte(ix) == 0x21 && r.Byte(ix + 1) == 0xF9) ix += 8; else if (r.Byte(ix) == 0x21 && r.Byte(ix + 1) == 0xFE) { const char *commentEnd = r.Find(ix + 2, 0x00); ix = commentEnd ? commentEnd - data + 1 : len; } else if (r.Byte(ix) == 0x21 && r.Byte(ix + 1) == 0x01 && ix + 15 < len) { const char *textDataEnd = r.Find(ix + 15, 0x00); ix = textDataEnd ? textDataEnd - data + 1 : len; } else if (r.Byte(ix) == 0x21 && r.Byte(ix + 1) == 0xFF && ix + 14 < len) { const char *applicationDataEnd = r.Find(ix + 14, 0x00); ix = applicationDataEnd ? applicationDataEnd - data + 1 : len; } else break; } } break; case Img_JPEG: // find the last start of frame marker for non-differential Huffman/arithmetic coding for (size_t ix = 2; ix + 9 < len && r.Byte(ix) == 0xFF; ) { if (0xC0 <= r.Byte(ix + 1) && r.Byte(ix + 1) <= 0xC3 || 0xC9 <= r.Byte(ix + 1) && r.Byte(ix + 1) <= 0xCB) { result.Width = r.WordBE(ix + 7); result.Height = r.WordBE(ix + 5); } ix += r.WordBE(ix + 2) + 2; } break; case Img_JXR: case Img_TIFF: if (len >= 10) { bool isBE = r.Byte(0) == 'M', isJXR = r.Byte(2) == 0xBC; CrashIf(!isBE && r.Byte(0) != 'I' || isJXR && isBE); const WORD WIDTH = isJXR ? 0xBC80 : 0x0100, HEIGHT = isJXR ? 0xBC81 : 0x0101; size_t idx = r.DWord(4, isBE); WORD count = idx <= len - 2 ? r.Word(idx, isBE) : 0; for (idx += 2; count > 0 && idx <= len - 12; count--, idx += 12) { WORD tag = r.Word(idx, isBE), type = r.Word(idx + 2, isBE); if (r.DWord(idx + 4, isBE) != 1) continue; else if (WIDTH == tag && 4 == type) result.Width = r.DWord(idx + 8, isBE); else if (WIDTH == tag && 3 == type) result.Width = r.Word(idx + 8, isBE); else if (WIDTH == tag && 1 == type) result.Width = r.Byte(idx + 8); else if (HEIGHT == tag && 4 == type) result.Height = r.DWord(idx + 8, isBE); else if (HEIGHT == tag && 3 == type) result.Height = r.Word(idx + 8, isBE); else if (HEIGHT == tag && 1 == type) result.Height = r.Byte(idx + 8); } } break; case Img_PNG: if (len >= 24 && str::StartsWith(data + 12, "IHDR")) { result.Width = r.DWordBE(16); result.Height = r.DWordBE(20); } break; case Img_TGA: if (len >= 16) { result.Width = r.WordLE(12); result.Height = r.WordLE(14); } break; case Img_WebP: if (len >= 30 && str::StartsWith(data + 12, "VP8 ")) { result.Width = r.WordLE(26) & 0x3fff; result.Height = r.WordLE(28) & 0x3fff; } else { result = webp::SizeFromData(data, len); } break; case Img_JP2: if (len >= 32) { size_t ix = 0; while (ix < len - 32) { uint32_t lbox = r.DWordBE(ix); uint32_t tbox = r.DWordBE(ix + 4); if (0x6A703268 /* jp2h */ == tbox) { ix += 8; if (r.DWordBE(ix) == 24 && r.DWordBE(ix + 4) == 0x69686472 /* ihdr */) { result.Width = r.DWordBE(ix + 16); result.Height = r.DWordBE(ix + 12); } break; } else if (lbox != 0 && ix < UINT32_MAX - lbox) { ix += lbox; } else { break; } } } break; } if (result.Empty()) { // let GDI+ extract the image size if we've failed // (currently happens for animated GIF) Bitmap *bmp = BitmapFromData(data, len); if (bmp) result = Size(bmp->GetWidth(), bmp->GetHeight()); delete bmp; } return result; }
void jointColorDepthFillOcclusion_(const Mat& src, const Mat& guide, Mat& dest, const Size ksize, T threshold) { if (dest.empty())dest.create(src.size(), src.type()); Mat sim, gim; const int radiusw = ksize.width / 2; const int radiush = ksize.height / 2;; copyMakeBorder(src, sim, radiush, radiush, radiusw, radiusw, cv::BORDER_DEFAULT); copyMakeBorder(guide, gim, radiush, radiush, radiusw, radiusw, cv::BORDER_DEFAULT); vector<int> _space_ofs_before(ksize.area()); int* space_ofs_before = &_space_ofs_before[0]; int maxk = 0; for (int i = -radiush; i <= radiush; i++) { for (int j = -radiusw; j <= radiusw; j++) { double r = std::sqrt((double)i*i + (double)j*j); if (r > radiusw) continue; space_ofs_before[maxk++] = (int)(i*sim.cols + j); } } const int steps = sim.cols; const int step = dest.cols; T* sptr = sim.ptr<T>(radiush); sptr += radiusw; uchar* jptr = gim.ptr<uchar>(radiush); jptr += radiusw; T* dst = dest.ptr<T>(0); T th2 = threshold*threshold; for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { const T val0j = jptr[j]; int minv = INT_MAX; T mind = 0; if (sptr[j] == 0) { for (int k = 0; k < maxk; k++) { if (sptr[j + space_ofs_before[k]] == 0) continue; const T valj = jptr[j + space_ofs_before[k]]; int ab = (int)((valj - val0j)*(valj - val0j)); if (ab < minv) { minv = ab; mind = sptr[j + space_ofs_before[k]]; } } if (minv < th2) { dst[j] = mind; } } } sptr += steps; jptr += steps; dst += step; } }
static void CompressTextureSet( Vector<VirtFS::VirtDesc>& files, Pair<CString, ImageProcessor> const& file, FileProcessor* cooker, TerminalCursor& cursor) { Resource r(MkUrl(file.first.c_str())); auto outName = Path(file.first).removeExt(); PixCmp cmp = PixCmp::RGBA; BitFmt bfmt; Bytes data; Size size; auto decoder = image_decoders.find(file.second); if(decoder == image_decoders.end()) return; if(!decoder->second(cooker, file, cmp, bfmt, size, data, r)) return; if(size.area() == 0) { return; } texture_settings_t settings; settings.flags = stb::ImageHint::Undefined; settings.max_size = max_texture_size; settings.min_size = min_texture_size; settings.channels = 4; settings.formats = Compress_ALL; /*!< Use all formats by default */ settings.parse(outName); if(size.w < settings.max_size && size.h < settings.max_size) { std::tie(data, size) = ScaleImage( std::move(data), size, settings.max_size, settings.flags); } else if(size.w > settings.max_size && size.h > settings.max_size) { std::tie(data, size) = ScaleImage( std::move(data), size, settings.max_size, settings.flags); } common_tools_t tools = {cooker, cursor, settings, files}; if(settings.formats & Compress_DXT) CompressDXT(tools, file, size, data, outName); #if defined(HAVE_ETC2COMP) if(settings.formats & Compress_ETC) CompressETC12(tools, size, data, outName); #endif if(settings.formats & Compress_RAW) { /* Just export the raw image as RGBA8 */ IMG::serial_image img; img.size = size; img.v2.bit_fmt = BitFmt::UByte; img.v2.format.base_fmt = PixFmt::RGBA8; img.v2.format.c_flags = CompFlags::CompressionNone; img.v2.format.p_flags = PixFlg::None; auto rawName = outName.addExtension("raw"); files.emplace_back(rawName, Bytes(), 0); auto& rawData = files.back().data; rawData = Bytes::Alloc(sizeof(img) + data.size); MemCpy(Bytes::From(img), rawData.at(0, sizeof(img))); MemCpy(data, rawData.at(sizeof(img))); cursor.progress( TEXCOMPRESS_API "Exporting raw RGBA for {0}", file.first); } }
Line Line::normal() const { Size v = vector(); //return Line(-v.cy(),v.cx(),v.cy(),-v.cx()); return Line(0,0,v.cy(),-v.cx()); }
/** Greater-than-or-equal test. @param pt the other size to compare this to. @return true if the test is successful. */ bool operator >= (const Size<T> &pt) const { return getArea() >= pt.getArea(); }
void Widget::setSize(const Size &size) { setWidth(size.width()); setHeight(size.height()); }
void UITextEdit::update(bool focusCursor) { if(!m_updatesEnabled) return; std::string text = getDisplayedText(); m_drawText = text; int textLength = text.length(); // prevent glitches if(m_rect.isEmpty()) return; // map glyphs positions Size textBoxSize; const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize); const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords(); const Size *glyphsSize = m_font->getGlyphsSize(); int glyph; // update rect size if(!m_rect.isValid() || m_textHorizontalAutoResize || m_textVerticalAutoResize) { textBoxSize += Size(m_padding.left + m_padding.right, m_padding.top + m_padding.bottom) + m_textOffset.toSize(); Size size = getSize(); if(size.width() <= 0 || (m_textHorizontalAutoResize && !m_textWrap)) size.setWidth(textBoxSize.width()); if(size.height() <= 0 || m_textVerticalAutoResize) size.setHeight(textBoxSize.height()); setSize(size); } // resize just on demand if(textLength > (int)m_glyphsCoords.size()) { m_glyphsCoords.resize(textLength); m_glyphsTexCoords.resize(textLength); } Point oldTextAreaOffset = m_textVirtualOffset; if(textBoxSize.width() <= getPaddingRect().width()) m_textVirtualOffset.x = 0; if(textBoxSize.height() <= getPaddingRect().height()) m_textVirtualOffset.y = 0; // readjust start view area based on cursor position m_cursorInRange = false; if(focusCursor && m_autoScroll) { if(m_cursorPos > 0 && textLength > 0) { assert(m_cursorPos <= textLength); Rect virtualRect(m_textVirtualOffset, m_rect.size() - Size(m_padding.left+m_padding.right, 0)); // previous rendered virtual rect int pos = m_cursorPos - 1; // element before cursor glyph = (uchar)text[pos]; // glyph of the element before cursor Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]); // if the cursor is not on the previous rendered virtual rect we need to update it if(!virtualRect.contains(glyphRect.topLeft()) || !virtualRect.contains(glyphRect.bottomRight())) { // calculate where is the first glyph visible Point startGlyphPos; startGlyphPos.y = std::max<int>(glyphRect.bottom() - virtualRect.height(), 0); startGlyphPos.x = std::max<int>(glyphRect.right() - virtualRect.width(), 0); // find that glyph for(pos = 0; pos < textLength; ++pos) { glyph = (uchar)text[pos]; glyphRect = Rect(glyphsPositions[pos], glyphsSize[glyph]); glyphRect.setTop(std::max<int>(glyphRect.top() - m_font->getYOffset() - m_font->getGlyphSpacing().height(), 0)); glyphRect.setLeft(std::max<int>(glyphRect.left() - m_font->getGlyphSpacing().width(), 0)); // first glyph entirely visible found if(glyphRect.topLeft() >= startGlyphPos) { m_textVirtualOffset.x = glyphsPositions[pos].x; m_textVirtualOffset.y = glyphsPositions[pos].y - m_font->getYOffset(); break; } } } } else { m_textVirtualOffset = Point(0,0); } m_cursorInRange = true; } else { if(m_cursorPos > 0 && textLength > 0) { Rect virtualRect(m_textVirtualOffset, m_rect.size() - Size(2*m_padding.left+m_padding.right, 0) ); // previous rendered virtual rect int pos = m_cursorPos - 1; // element before cursor glyph = (uchar)text[pos]; // glyph of the element before cursor Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]); if(virtualRect.contains(glyphRect.topLeft()) && virtualRect.contains(glyphRect.bottomRight())) m_cursorInRange = true; } else { m_cursorInRange = true; } } bool fireAreaUpdate = false; if(oldTextAreaOffset != m_textVirtualOffset) fireAreaUpdate = true; Rect textScreenCoords = m_rect; textScreenCoords.expandLeft(-m_padding.left); textScreenCoords.expandRight(-m_padding.right); textScreenCoords.expandBottom(-m_padding.bottom); textScreenCoords.expandTop(-m_padding.top); m_drawArea = textScreenCoords; if(textScreenCoords.size() != m_textVirtualSize) { m_textVirtualSize = textScreenCoords.size(); fireAreaUpdate = true; } Size totalSize = textBoxSize; if(totalSize.width() < m_textVirtualSize.width()) totalSize.setWidth(m_textVirtualSize.height()); if(totalSize.height() < m_textVirtualSize.height()) totalSize.setHeight(m_textVirtualSize.height()); if(m_textTotalSize != totalSize) { m_textTotalSize = totalSize; fireAreaUpdate = true; } if(m_textAlign & Fw::AlignBottom) { m_drawArea.translate(0, textScreenCoords.height() - textBoxSize.height()); } else if(m_textAlign & Fw::AlignVerticalCenter) { m_drawArea.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2); } else { // AlignTop } if(m_textAlign & Fw::AlignRight) { m_drawArea.translate(textScreenCoords.width() - textBoxSize.width(), 0); } else if(m_textAlign & Fw::AlignHorizontalCenter) { m_drawArea.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0); } else { // AlignLeft } for(int i = 0; i < textLength; ++i) { glyph = (uchar)text[i]; m_glyphsCoords[i].clear(); // skip invalid glyphs if(glyph < 32 && glyph != (uchar)'\n') continue; // calculate initial glyph rect and texture coords Rect glyphScreenCoords(glyphsPositions[i], glyphsSize[glyph]); Rect glyphTextureCoords = glyphsTextureCoords[glyph]; // first translate to align position if(m_textAlign & Fw::AlignBottom) { glyphScreenCoords.translate(0, textScreenCoords.height() - textBoxSize.height()); } else if(m_textAlign & Fw::AlignVerticalCenter) { glyphScreenCoords.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2); } else { // AlignTop // nothing to do } if(m_textAlign & Fw::AlignRight) { glyphScreenCoords.translate(textScreenCoords.width() - textBoxSize.width(), 0); } else if(m_textAlign & Fw::AlignHorizontalCenter) { glyphScreenCoords.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0); } else { // AlignLeft // nothing to do } // only render glyphs that are after startRenderPosition if(glyphScreenCoords.bottom() < m_textVirtualOffset.y || glyphScreenCoords.right() < m_textVirtualOffset.x) continue; // bound glyph topLeft to startRenderPosition if(glyphScreenCoords.top() < m_textVirtualOffset.y) { glyphTextureCoords.setTop(glyphTextureCoords.top() + (m_textVirtualOffset.y - glyphScreenCoords.top())); glyphScreenCoords.setTop(m_textVirtualOffset.y); } if(glyphScreenCoords.left() < m_textVirtualOffset.x) { glyphTextureCoords.setLeft(glyphTextureCoords.left() + (m_textVirtualOffset.x - glyphScreenCoords.left())); glyphScreenCoords.setLeft(m_textVirtualOffset.x); } // subtract startInternalPos glyphScreenCoords.translate(-m_textVirtualOffset); // translate rect to screen coords glyphScreenCoords.translate(textScreenCoords.topLeft()); // only render if glyph rect is visible on screenCoords if(!textScreenCoords.intersects(glyphScreenCoords)) continue; // bound glyph bottomRight to screenCoords bottomRight if(glyphScreenCoords.bottom() > textScreenCoords.bottom()) { glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (textScreenCoords.bottom() - glyphScreenCoords.bottom())); glyphScreenCoords.setBottom(textScreenCoords.bottom()); } if(glyphScreenCoords.right() > textScreenCoords.right()) { glyphTextureCoords.setRight(glyphTextureCoords.right() + (textScreenCoords.right() - glyphScreenCoords.right())); glyphScreenCoords.setRight(textScreenCoords.right()); } // render glyph m_glyphsCoords[i] = glyphScreenCoords; m_glyphsTexCoords[i] = glyphTextureCoords; } if(fireAreaUpdate) onTextAreaUpdate(m_textVirtualOffset, m_textVirtualSize, m_textTotalSize); g_app.repaint(); }
/** Creates a subbitmap. @param parent parent bitmap. @param pt point of origin in the parent. @param size size. */ Bitmap(Bitmap &parent, const Point<int> &pt, const Size<int> &size) : Shared(al_create_sub_bitmap(parent.get(), pt.getX(), pt.getY(), size.getWidth(), size.getHeight()), al_destroy_bitmap) { }
void NodeHorizontal::updateLeftPos() { using namespace NodeHorizontal_impl; Size leftSize = left->getCurrentSize(); left->setCurrentTopLeft(Point(leftPadding,(getCurrentSize().height()-topPadding-bottomPadding-leftSize.height())/2+topPadding),getCurrentSize()); }
void* get() { if (size_.bytes() == 0) return 0; return &data[0]; }
Point computeRightChildPoint(Point const&myNotationPos,Size const¬ationSize) { return myNotationPos+Point(notationSize.width()+middlePadding,0); }
Point computeLeftChildPos(Size const&mySize,Size const&leftSize) { return Point(0,(mySize.height()-leftSize.height())/2);//垂直方向上居中 }
void MediaDispatcherImpl::OnVideoDecode(VideoDecodeEventArgs const& e) { VideoSource const& s = e.Source(); int32 channel = s.Channel(); if (mNeedWaitI[channel]) { if (s.Type() == VideoType_H264_IFrame) mNeedWaitI[channel] = false; else return; } if (mVideoMode[channel].Width() != s.Width() || mVideoMode[channel].Height() != s.Height()) { mVideoMode[channel] = Size(s.Width(), s.Height()); DisposeVideoDecoder(channel); } JNIEnv* env = GetJNIEnvAttachThread(); if (mVideoDecoder[channel] == NULL) CreateVideoDecoder(env, channel, s.Width(), s.Height()); // create decoder and buffer jintArray outputSize = env->NewIntArray(3); jint* _outputSize = env->GetIntArrayElements(outputSize, 0); // jobjectArray videoBuffer2d = env->CallObjectMethod(mObject, methods.MediaDispatcher_GetVideoBuffer, channel); jbyteArray y = (jbyteArray) env->GetObjectArrayElement(videoBuffer2d, (jsize) 0); jbyteArray u = (jbyteArray) env->GetObjectArrayElement(videoBuffer2d, (jsize) 1); jbyteArray v = (jbyteArray) env->GetObjectArrayElement(videoBuffer2d, (jsize) 2); jbyte* _y = env->GetByteArrayElements(y, 0); // not copy array jbyte* _u = env->GetByteArrayElements(u, 0); // not copy array jbyte* _v = env->GetByteArrayElements(v, 0); // not copy array byte* output[3] = { (byte*) _y, (byte*) _u, (byte*) _v }; // Size resolution; Thickness cropped; if (mVideoDecoder[channel]->Decode(s.Buffer(), s.BufferLength(), VideoPixelFormat_I420, output, _outputSize, resolution, cropped)) { int32 dstW = resolution.Width() - cropped.Left() - cropped.Right(); int32 dstH = resolution.Height() - cropped.Top() - cropped.Bottom(); env->CallVoidMethod(mObject, methods.VideoFrameDecodedListener_OnVideoOneFrameDecoded, channel, dstW, dstH, outputSize, VideoPixelFormat_I420, cropped.Left(), cropped.Top()); } else { LOGE("decode error: %d", channel); } env->ReleaseByteArrayElements(y, _y, 0); env->ReleaseByteArrayElements(u, _u, 0); env->ReleaseByteArrayElements(v, _v, 0); env->DeleteLocalRef(y); env->DeleteLocalRef(u); env->DeleteLocalRef(v); env->DeleteLocalRef(videoBuffer2d); env->ReleaseIntArrayElements(outputSize, _outputSize, 0); // mode: copy back the content and free the elems buffer // MUST to free the elems buffer, because elems buffer holds // a local reference, which needs to delete env->DeleteLocalRef(outputSize); JNIEnvDeattachThread(); }
void X11Window::resize(const Size& size) { XResizeWindow(m_display, m_window, size.width(), size.height()); }
bool RasterCoverageConnector::store(IlwisObject *obj, const IOOptions & ) { if(!loadDriver()) return false; RasterCoverage *raster = static_cast<RasterCoverage *>(obj); DataDefinition currentDef = raster->datadefRef(); if (! hasType(raster->datadef().domain()->ilwisType(),itNUMERICDOMAIN | itCOLORDOMAIN)){ IDomain dom; QString code = raster->datadef().domain()->ilwisType() == itITEMDOMAIN ? "code=count" : "code=value"; if(!dom.prepare(code)) { //TODO: for the moment only value maps in gdal return ERROR1(ERR_NO_INITIALIZED_1,obj->name()); } currentDef.domain(dom); } Size<> sz = raster->size(); GDALDataType gdalType = ilwisType2GdalType(currentDef.range()->valueType()); QString filename = constructOutputName(_driver); bool isColorMap = currentDef.domain()->ilwisType() == itCOLORDOMAIN; bool ispaletteMap = currentDef.domain()->valueType() == itPALETTECOLOR; GDALDatasetH dataset = 0; if ( ispaletteMap && format() == "GTiff"){ char *options[] = {"PHOTOMETRIC=PALETTE", NULL}; dataset = gdal()->create( _driver, filename.toLocal8Bit(), sz.xsize(), sz.ysize(), sz.zsize(), gdalType, options ); }else dataset = gdal()->create( _driver, filename.toLocal8Bit(), sz.xsize(), sz.ysize(), isColorMap ? sz.zsize() * 3 : sz.zsize(), gdalType, 0 ); if ( dataset == 0) { return ERROR2(ERR_COULDNT_CREATE_OBJECT_FOR_2, "data set",_fileUrl.toLocalFile()); } bool ok = setGeotransform(raster, dataset); if (ok) ok = setSRS(raster, dataset); if (!ok) return false; if ( isColorMap ){ ok = storeColorRaster(raster, dataset) ; } else { switch(gdalType) { case GDT_Byte: ok = save<quint8>(raster, dataset,gdalType);break; case GDT_UInt16: ok = save<quint16>(raster, dataset,gdalType);break; case GDT_Int16: ok = save<qint16>(raster, dataset,gdalType);break; case GDT_Int32: ok = save<qint32>(raster, dataset,gdalType);break; case GDT_UInt32: ok = save<quint32>(raster, dataset,gdalType);break; case GDT_Float32: ok = save<float>(raster, dataset,gdalType);break; case GDT_Float64: ok = save<double>(raster, dataset,gdalType);break; default: ok= ERROR1(ERR_NO_INITIALIZED_1, "gdal Data type"); } } gdal()->close(dataset); return ok; }
/** Creates a bitmap of the specific size. @param size size. */ Bitmap(const Size<int> &size) : Shared(al_create_bitmap(size.getWidth(), size.getHeight()), al_destroy_bitmap) { }
/*! \fn swUiControl::Resize( const Size& newSize ) */ void swUiControl::Resize( const Size& newSize ) { SetGeometry(Rect ( m_geometry.x(), m_geometry.y(), newSize.width(), newSize.height() )); }
llvm::Constant * IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, SILLocation diagLoc) { // See if we already emitted this. auto found = KeyPathPatterns.find(pattern); if (found != KeyPathPatterns.end()) return found->second; // Gather type arguments from the root and leaf types of the key path. auto rootTy = pattern->getRootType(); auto valueTy = pattern->getValueType(); // Check for parameterization, whether by subscript indexes or by the generic // environment. If there isn't any, we can instantiate the pattern in-place. bool isInstantiableInPlace = pattern->getNumOperands() == 0 && !pattern->getGenericSignature(); // Collect the required parameters for the keypath's generic environment. SmallVector<GenericRequirement, 4> requirements; GenericEnvironment *genericEnv = nullptr; if (auto sig = pattern->getGenericSignature()) { genericEnv = sig->createGenericEnvironment(*getSwiftModule()); enumerateGenericSignatureRequirements(pattern->getGenericSignature(), [&](GenericRequirement reqt) { requirements.push_back(reqt); }); } /// Generate a metadata accessor that produces metadata for the given type /// using arguments from the generic context of the key path. auto emitMetadataGenerator = [&](CanType type) -> llvm::Function * { // TODO: Use the standard metadata accessor when there are no arguments // and the metadata accessor is defined. // Build a stub that loads the necessary bindings from the key path's // argument buffer then fetches the metadata. auto fnTy = llvm::FunctionType::get(TypeMetadataPtrTy, {Int8PtrTy}, /*vararg*/ false); auto accessorThunk = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage, "keypath_get_type", getModule()); accessorThunk->setAttributes(constructInitialAttributes()); { IRGenFunction IGF(*this, accessorThunk); if (DebugInfo) DebugInfo->emitArtificialFunction(IGF, accessorThunk); if (type->hasTypeParameter()) { auto bindingsBufPtr = IGF.collectParameters().claimNext(); bindFromGenericRequirementsBuffer(IGF, requirements, Address(bindingsBufPtr, getPointerAlignment()), [&](CanType t) { if (!genericEnv) return t; return genericEnv->mapTypeIntoContext(t)->getCanonicalType(); }); type = genericEnv->mapTypeIntoContext(type)->getCanonicalType(); } auto ret = IGF.emitTypeMetadataRef(type); IGF.Builder.CreateRet(ret); } return accessorThunk; }; // Start building the key path pattern. ConstantInitBuilder builder(*this); ConstantStructBuilder fields = builder.beginStruct(); fields.setPacked(true); // Add a zero-initialized header we can use for lazy initialization. fields.add(llvm::ConstantInt::get(SizeTy, 0)); #ifndef NDEBUG auto startOfObject = fields.getNextOffsetFromGlobal(); #endif // Store references to metadata generator functions to generate the metadata // for the root and leaf. These sit in the "isa" and object header parts of // the final object. fields.add(emitMetadataGenerator(rootTy)); fields.add(emitMetadataGenerator(valueTy)); // TODO: 32-bit still has a padding word if (SizeTy == Int32Ty) { fields.addInt32(0); } #ifndef NDEBUG auto endOfObjectHeader = fields.getNextOffsetFromGlobal(); unsigned expectedObjectHeaderSize; if (SizeTy == Int64Ty) expectedObjectHeaderSize = SWIFT_ABI_HEAP_OBJECT_HEADER_SIZE_64; else if (SizeTy == Int32Ty) expectedObjectHeaderSize = SWIFT_ABI_HEAP_OBJECT_HEADER_SIZE_32; else llvm_unreachable("unexpected pointer size"); assert((endOfObjectHeader - startOfObject).getValue() == expectedObjectHeaderSize && "key path pattern header size doesn't match heap object header size"); #endif // Add a pointer to the ObjC KVC compatibility string, if there is one, or // null otherwise. llvm::Constant *objcString; if (!pattern->getObjCString().empty()) { objcString = getAddrOfGlobalString(pattern->getObjCString()); } else { objcString = llvm::ConstantPointerNull::get(Int8PtrTy); } fields.add(objcString); // Leave a placeholder for the buffer header, since we need to know the full // buffer size to fill it in. auto headerPlaceholder = fields.addPlaceholderWithSize(Int32Ty); auto startOfKeyPathBuffer = fields.getNextOffsetFromGlobal(); // Build out the components. auto baseTy = rootTy; auto getPropertyOffsetOrIndirectOffset = [&](SILType loweredBaseTy, VarDecl *property) -> std::pair<llvm::Constant*, bool> { llvm::Constant *offset; bool isResolved; bool isStruct; if (loweredBaseTy.getStructOrBoundGenericStruct()) { offset = emitPhysicalStructMemberFixedOffset(*this, loweredBaseTy, property); isStruct = true; } else if (loweredBaseTy.getClassOrBoundGenericClass()) { offset = tryEmitConstantClassFragilePhysicalMemberOffset(*this, loweredBaseTy, property); isStruct = false; } else { llvm_unreachable("property of non-struct, non-class?!"); } // If the offset isn't fixed, try instead to get the field offset vector // offset for the field to look it up dynamically. isResolved = offset != nullptr; if (!isResolved) { if (isStruct) { offset = emitPhysicalStructMemberOffsetOfFieldOffset( *this, loweredBaseTy, property); assert(offset && "field is neither fixed-offset nor in offset vector"); } else { auto offsetValue = getClassFieldOffset(*this, loweredBaseTy.getClassOrBoundGenericClass(), property); offset = llvm::ConstantInt::get(Int32Ty, offsetValue.getValue()); } } return {offset, isResolved}; }; for (unsigned i : indices(pattern->getComponents())) { SILType loweredBaseTy; Lowering::GenericContextScope scope(getSILTypes(), pattern->getGenericSignature()); loweredBaseTy = getLoweredType(AbstractionPattern::getOpaque(), baseTy->getLValueOrInOutObjectType()); auto &component = pattern->getComponents()[i]; switch (auto kind = component.getKind()) { case KeyPathPatternComponent::Kind::StoredProperty: { // Try to get a constant offset if we can. auto property = cast<VarDecl>(component.getStoredPropertyDecl()); llvm::Constant *offset; bool isResolved; std::tie(offset, isResolved) = getPropertyOffsetOrIndirectOffset(loweredBaseTy, property); offset = llvm::ConstantExpr::getTruncOrBitCast(offset, Int32Ty); bool isStruct = (bool)loweredBaseTy.getStructOrBoundGenericStruct(); // If the projection is a statically known integer, try to pack it into // the key path payload. if (isResolved) { if (auto offsetInt = dyn_cast_or_null<llvm::ConstantInt>(offset)) { auto offsetValue = offsetInt->getValue().getZExtValue(); if (KeyPathComponentHeader::offsetCanBeInline(offsetValue)) { auto header = isStruct ? KeyPathComponentHeader::forStructComponentWithInlineOffset(offsetValue) : KeyPathComponentHeader::forClassComponentWithInlineOffset(offsetValue); fields.addInt32(header.getData()); break; } } auto header = isStruct ? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset() : KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(); fields.addInt32(header.getData()); fields.add(offset); } else { // Otherwise, stash the offset of the field offset within the metadata // object, so we can pull it out at instantiation time. // TODO: We'll also need a way to handle resilient field offsets, once // field offset vectors no longer cover all fields in the type. KeyPathComponentHeader header = isStruct ? KeyPathComponentHeader::forStructComponentWithUnresolvedOffset() : KeyPathComponentHeader::forClassComponentWithUnresolvedOffset(); fields.addInt32(header.getData()); fields.add(offset); } break; } case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: { // Encode the settability. bool settable = kind == KeyPathPatternComponent::Kind::SettableProperty; KeyPathComponentHeader::ComputedPropertyKind componentKind; if (settable) { componentKind = component.isComputedSettablePropertyMutating() ? KeyPathComponentHeader::SettableMutating : KeyPathComponentHeader::SettableNonmutating; } else { componentKind = KeyPathComponentHeader::GetOnly; } // Lower the id reference. auto id = component.getComputedPropertyId(); KeyPathComponentHeader::ComputedPropertyIDKind idKind; llvm::Constant *idValue; bool idResolved; switch (id.getKind()) { case KeyPathPatternComponent::ComputedPropertyId::Function: idKind = KeyPathComponentHeader::Pointer; idValue = getAddrOfSILFunction(id.getFunction(), NotForDefinition); idResolved = true; break; case KeyPathPatternComponent::ComputedPropertyId::DeclRef: { auto declRef = id.getDeclRef(); // Foreign method refs identify using a selector // reference, which is doubly-indirected and filled in with a unique // pointer by dyld. if (declRef.isForeign) { assert(ObjCInterop && "foreign keypath component w/o objc interop?!"); idKind = KeyPathComponentHeader::Pointer; idValue = getAddrOfObjCSelectorRef(declRef); idResolved = false; } else { idKind = KeyPathComponentHeader::VTableOffset; auto dc = declRef.getDecl()->getDeclContext(); if (isa<ClassDecl>(dc)) { auto index = getVirtualMethodIndex(*this, declRef); idValue = llvm::ConstantInt::get(SizeTy, index); idResolved = true; } else if (auto methodProto = dyn_cast<ProtocolDecl>(dc)) { auto &protoInfo = getProtocolInfo(methodProto); auto index = protoInfo.getFunctionIndex( cast<AbstractFunctionDecl>(declRef.getDecl())); idValue = llvm::ConstantInt::get(SizeTy, -index.getValue()); idResolved = true; } else { llvm_unreachable("neither a class nor protocol dynamic method?"); } } break; } case KeyPathPatternComponent::ComputedPropertyId::Property: idKind = KeyPathComponentHeader::StoredPropertyOffset; std::tie(idValue, idResolved) = getPropertyOffsetOrIndirectOffset(loweredBaseTy, id.getProperty()); idValue = llvm::ConstantExpr::getZExtOrBitCast(idValue, SizeTy); break; } auto header = KeyPathComponentHeader::forComputedProperty(componentKind, idKind, !isInstantiableInPlace, idResolved); fields.addInt32(header.getData()); fields.add(idValue); if (isInstantiableInPlace) { // No generic arguments, so we can invoke the getter/setter as is. fields.add(getAddrOfSILFunction(component.getComputedPropertyGetter(), NotForDefinition)); if (settable) fields.add(getAddrOfSILFunction(component.getComputedPropertySetter(), NotForDefinition)); } else { // If there's generic context (TODO: or subscript indexes), embed as // arguments in the component. Thunk the SIL-level accessors to give the // runtime implementation a polymorphically-callable interface. Context.Diags.diagnose(diagLoc.getSourceLoc(), diag::not_implemented, "generic computed key paths"); return llvm::UndefValue::get(Int8PtrTy); } } } // For all but the last component, we pack in the type of the component. if (i + 1 != pattern->getComponents().size()) { fields.add(emitMetadataGenerator(component.getComponentType())); } baseTy = component.getComponentType(); } // Save the total size of the buffer. Size componentSize = fields.getNextOffsetFromGlobal() - startOfKeyPathBuffer; // We now have enough info to build the header. KeyPathBufferHeader header(componentSize.getValue(), isInstantiableInPlace, /*reference prefix*/ false); // Add the header, followed by the components. fields.fillPlaceholder(headerPlaceholder, llvm::ConstantInt::get(Int32Ty, header.getData())); // Create the global variable. // TODO: The pattern could be immutable if // it isn't instantiable in place, and if we made the type metadata accessor // references private, it could go in true-const memory. auto patternVar = fields.finishAndCreateGlobal("keypath", getPointerAlignment(), /*constant*/ false, llvm::GlobalVariable::PrivateLinkage); KeyPathPatterns.insert({pattern, patternVar}); return patternVar; }
/** Less-than test. @param pt the other size to compare this to. @return true if the test is successful. */ bool operator < (const Size<T> &pt) const { return getArea() < pt.getArea(); }