- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I rewrote the saveas function, but it didn't work.
@daniel_cadext @tbrammer @Alexander.Rivilis
Solved! Go to Solution.
I rewrote the saveas function, but it didn't work.
@daniel_cadext @tbrammer @Alexander.Rivilis
Solved! Go to Solution.
May I ask if there is an answer to this question?
Hi,
Yes, this is possible, here is the minimal code that I used to test it now.
First, let's define a custom entity.
class ImageWorldDraw : public AcDbEntity
{
public :
ACRX_DECLARE_MEMBERS(ImageWorldDraw);
ImageWorldDraw();
~ImageWorldDraw();
virtual Adesk::Boolean subWorldDraw(AcGiWorldDraw*);
virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* filer);
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* filer) const;
virtual void saveAs(AcGiWorldDraw* mode, AcDb::SaveType st);
private:
std::unique_ptr<AcGiPixelBGRA32[]> m_imageSourceData;
};
Now, let's implement the custom entity.
ACRX_DXF_DEFINE_MEMBERS(ImageWorldDraw, AcDbEntity,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,
0, IMAGE_WORLD_DRAW, ImgSamp);
ImageWorldDraw::ImageWorldDraw()
{
}
ImageWorldDraw::~ImageWorldDraw()
{
}
Adesk::Boolean ImageWorldDraw::subWorldDraw(AcGiWorldDraw* mode)
{
const int imageWidth = 100;
const int imageHeight = 200;
const int imageSize = imageWidth * imageHeight;
// Allocate and fill image data if not already set
if (!m_imageSourceData)
{
m_imageSourceData = std::make_unique<AcGiPixelBGRA32[]>(imageSize);
for (int i = 0; i < imageSize; ++i)
{
//Setting a gradient effect
m_imageSourceData[i].setRGBA((i % imageWidth) * 255 / imageWidth, // Red
(i / imageWidth) * 255 / imageHeight, // Green
128, // Blue
255); // Alpha
}
}
// Use the image data to create an AcGiImageBGRA32 object
AcGiImageBGRA32 imageSource(imageWidth, imageHeight, m_imageSourceData.get());
// Define image placement and size in world coordinates
AcGePoint3d position(4.0, 5.0, 6.0);
Adesk::UInt32 width = 200;
Adesk::UInt32 height = 400;
AcGeVector3d u(width, 0.0, 0.0); // Horizontal vector
AcGeVector3d v(0.0, height, 0.0); // Vertical vector
AcGiGeometry::TransparencyMode transparencyMode = AcGiGeometry::kTransparencyOff;
// Draw the image
return mode->rawGeometry()->image(imageSource, position, u, v, transparencyMode);
}
Acad::ErrorStatus ImageWorldDraw::dwgInFields(AcDbDwgFiler* filer)
{
assertWriteEnabled();
Acad::ErrorStatus es = AcDbEntity::dwgInFields(filer);
if (es != Acad::eOk)
return es;
// Read image dimensions
Adesk::UInt32 width, height;
filer->readUInt32(&width);
filer->readUInt32(&height);
// Read image data
const size_t imageSize = width * height;
if (imageSize > 0)
{
m_imageSourceData = std::make_unique<AcGiPixelBGRA32[]>(imageSize);
for (size_t i = 0; i < imageSize; ++i)
{
filer->readBytes(&m_imageSourceData[i], sizeof(AcGiPixelBGRA32));
}
}
return filer->filerStatus();
}
Acad::ErrorStatus ImageWorldDraw::dwgOutFields(AcDbDwgFiler* filer) const
{
Acad::ErrorStatus es = AcDbEntity::dwgOutFields(filer);
if (es != Acad::eOk)
return es;
// Write image dimensions
Adesk::UInt32 width = 100;
Adesk::UInt32 height = 200;
filer->writeUInt32(width);
filer->writeUInt32(height);
// Write image data
const size_t imageSize = width * height;
if (imageSize > 0 && m_imageSourceData)
{
for (size_t i = 0; i < imageSize; ++i)
{
filer->writeBytes(&m_imageSourceData[i], sizeof(AcGiPixelBGRA32));
}
}
return filer->filerStatus();
}
void ImageWorldDraw::saveAs(AcGiWorldDraw* mode, AcDb::SaveType st)
{
if (st == AcDb::SaveType::k2018Save)
{
AcGePoint3d position(4.0, 5.0, 6.0);
Adesk::UInt32 width = 100;
Adesk::UInt32 height = 200;
AcGeVector3d u(width, 0.0, 0.0);
AcGeVector3d v(0.0, height, 0.0);
AcGiGeometry::TransparencyMode transparencyMode = AcGiGeometry::kTransparencyOff;
if (m_imageSourceData)
{
AcGiImageBGRA32 imageSource(width, height, m_imageSourceData.get());
mode->rawGeometry()->image(imageSource, position, u, v, transparencyMode);
}
}
}
Now, write the driver code to call over custom entity -
void imageTest() {
ImageWorldDraw* pImageWorldDraw = new ImageWorldDraw;
AcDbObjectId imageOID;
Acad::ErrorStatus es = postToDatabase(pImageWorldDraw, imageOID);
if (es != Acad::eOk) {
acutPrintf(_T("\nFailed to post the image to the database"));
delete pImageWorldDraw;
return;
}
pImageWorldDraw->close();}
Compile everything, load the DBX, and run the command imagetest
. You should see a gradient image. Save the drawing in the 2018 format.
Close AutoCAD.
Do not load the DBX; simply open the drawing. You should see something similar.
Note: The image pixel data is being stored in a buffer, which will increase the size of the drawing.
The code is working properly. If the dwg version is lower than 2018 and cannot be displayed, does st==AcDb:: SaveType:: k2018Save have to be the dwg version of 2018 to be displayed?
Yes, and make sure if DBX is also on current drawing format which is 2018.
If you want to support previous versions, make DBX is catered to that.
This macro caters DBX to current version of drawing.
ACRX_DXF_DEFINE_MEMBERS(
ImageWorldDraw,
AcDbEntity,
AcDb::kDHL_CURRENT,
AcDb::kMReleaseCurrent,
0,
IMAGE_WORLD_DRAW,
ImgSamp);
And, accordingly, you may have to implement for other save types too.
The proxy entity can only be displayed normally if the CAD version is greater than 2010.
You mean to say, if savetype > k2010, ie. it is working for only k2013Save, k2018Save ?
Can you please elaborate, if I understood incorrectly.
int width, height, channels;
unsigned char* data = stbi_load(imgPath, &width, &height, &channels, STBI_rgb_alpha);
if (data)
{
m_width = width;m_height = height;
size_t dataSize = width * height * STBI_rgb_alpha;
m_Source = new unsigned char[dataSize];
memcpy(m_Source,data,dataSize);
stbi_image_free(data);
}
for (int y = 0; y < m_height; ++y)
{
int srcRowIndex = (m_height- 1 - y) * m_width * 4;
for (int x = 0; x < m_width ; ++x)
{
int srcIndex = srcRowIndex + x * 4;
m_imageData[y * m_width + x].setBGRA(m_Source [srcIndex + 2], m_Source [srcIndex + 1], m_Source [srcIndex + 0], m_Source [srcIndex + 3]);
}
}
AcGiImageBGRA32 imageSource(m_width , m_curH, m_imageData);
mode->geometry().image(imageSource, position, u, v, AcGiGeometry::kTransparency8Bit);
I use stb_image to read the 1234.bmp file, and the displayed image has too obvious pixelation.
How can it be like a raster image or ole image?
You can use AcDbDwgFiler::readBytes and AcDbDwgFiler::writeBytes to store files directly inside your custom objects - so you could store the image file directly, rather than the bitmap data. Of course, your DBX would need to be able to extract and reconstitute it so you'd need to store the original name and size too.
What I mean is the user's CAD version, not the version of the DWG file. When the user's version is less than CAD2010, images are not displayed in the proxy entity.
How can it be like a raster image or ole image? The current display effect is not as good as raster images or OLE images in CAD.
You could extract the image file and insert it as a raster image... but that's a lot of work. Is there a reason why you want a raster as the proxy representation of your object? Depending on what your object does, is it not easier to attach extra properties to a raster image in the first place?
Can AcGiWorldGeometry:: image use Atil:: Is Image * used as a parameter?
My goal is to be able to view OLE images as they are when sent to others, but I cannot insert OLE images through programming methods and can only use UI interaction.
No, you can't use Atil in AcGiWorldGeomerty.
What is the problem you are trying to solve my understanding is you want display proxy graphics for images when your DBX is absent.
Yes, using AcGiWorldGemetry with there will be pixelated beyond certain zoom level.
I tried with OpenCV to read bitmap and compute Pixel data and ATIL, I don't see much difference.
Here is the code that I have played to implement both proxy graphics for image and ATIL based implementation.
AutoCAD Custom Raster Image Handling: Embedding and Display Techniques
I don't what to negate all the efforts being put into 'graphics in proxy objects', but you can create the OLE objects in code, despite what the documentation says:
//------------------------------------------------------------------------------
AcDbObjectId InsertOLEObject(CString sSourceFile, AcDbExtents* pExtents)
//------------------------------------------------------------------------------
{
// Using the given file, create an OLE object and insert it into the design at the user specified size and position
Acad::ErrorStatus es = Acad::eOk;
AcDbObjectId OLEid = AcDbObjectId::kNull;
AcDbTransactionManager* pTM = acdbTransactionManager;
if (pTM != NULL)
{
pTM->startTransaction();
AcDbOle2Frame* pNewFrame = new AcDbOle2Frame();
pNewFrame->setDatabaseDefaults();
AcDbDatabase* db = acdbHostApplicationServices()->workingDatabase();
AcDbBlockTable* blockTable;
es = db->getBlockTable(blockTable, AcDb::kForRead);
AcDbBlockTableRecord* modelSpace;
es = blockTable->getAt(ACDB_MODEL_SPACE, modelSpace, AcDb::kForWrite);
if (es == Acad::eWasOpenForWrite || es == Acad::eWasOpenForRead)
{
modelSpace = pApp->GetTableRecord();
}
es = modelSpace->appendAcDbEntity(pNewFrame);
es = acdbTransactionManager->addNewlyCreatedDBRObject(pNewFrame);
es = modelSpace->close();
es = blockTable->close();
AcApDocument* pActiveDoc = acDocManager->mdiActiveDocument();
COleDocument* pDoc = (COleDocument*)pActiveDoc->cDoc();
COleClientItem* pItem = new COleClientItem(pDoc);
if (pItem->CreateFromFile(sSourceFile))
{
es = pNewFrame->setOleClientItem(pItem);
CSize size(0, 0);
if (pItem->GetCachedExtent(&size))
{
// OLE returns the extent in HIMETRIC units -- we need pixels
CClientDC dc(NULL);
dc.HIMETRICtoDP(&size);
}
double dRatio = (double)size.cy / (double)size.cx;
es = pNewFrame->downgradeOpen();
es = pNewFrame->upgradeOpen();
CRectangle3d rect3d;
// Get user placement, using the ratio we've just established
if (pExtents != NULL)
{
// If we have a size, we'll use the width specified and adjust the height base on the calculated ratio
// We'll keep the top left as the anchor position
AcGePoint3d maxPt = pExtents->maxPoint();
AcGePoint3d minPt = pExtents->minPoint();
if (fabs(maxPt.x - minPt.x) > 0)
{
double dWidth = maxPt.x - minPt.x;
rect3d.upLeft = AcGePoint3d(minPt.x, maxPt.y, 0.0L);
rect3d.upRight = AcGePoint3d(maxPt.x, maxPt.y, 0.0L);
rect3d.lowLeft = AcGePoint3d(minPt.x, maxPt.y - (dWidth * dRatio), 0.0L);
rect3d.lowRight = AcGePoint3d(maxPt.x, maxPt.y - (dWidth * dRatio), 0.0L);
}
}
else
{
RectJig* pJig = new RectJig(dRatio);
if (pJig != NULL)
{
if (pJig->DoJig())
{
// Get size/position, same for everything
AcGePoint3d sp, ep, sp_wcs, ep_wcs;
if (pJig->GetRectPoints(sp, ep))
{
// Ensure we have something sensible
if (fabs(ep.x - sp.x) > 0 && fabs(ep.y - sp.y) > 0)
{
acdbUcs2Wcs(asDblArray(sp), asDblArray(sp_wcs), false);
acdbUcs2Wcs(asDblArray(ep), asDblArray(ep_wcs), false);
// Set the insert point
pNewFrame->setLocation(sp_wcs);
// Create the rectangle based on the size from the server
rect3d.lowLeft = sp_wcs;
rect3d.lowRight = sp_wcs;
rect3d.lowRight.x = ep_wcs.x;
rect3d.upLeft = sp_wcs;
rect3d.upLeft.y = ep_wcs.y;
rect3d.upRight = ep_wcs;
}
}
}
delete pJig;
}
}
// Set the position
pNewFrame->setPosition(rect3d);
OLEid = pNewFrame->objectId();
pItem->Close();
}
// Clean up
es = pNewFrame->close();
es = pTM->endTransaction();
}
return OLEid;
}
The result obtained from the above code is like this. It seems that it is not possible to directly create AcDbOle2Frame to insert OLE images, only the Insert menu can be used to add them. You can use the attached image to test it.
As for any OLE object, you need to have an OLE server registered as the handler for BMP files. Set your files to open with MS Paint.
@dziwve67853 wrote:
The result obtained from the above code is like this. It seems that it is not possible to directly create AcDbOle2Frame to insert OLE images, only the Insert menu can be used to add them. You can use the attached image to test it.
Set your files to open with MS Paint.
Is there a way to implement it through programming code instead of manually opening the image with MS Paint?
The 'CreateFromFile' method uses the Windows file association to create the OLE object. I guess you could do that in code, then restore it afterwards. You might also look at using 'CreateFromClipboard' instead, but getting the file onto the clipboard is a mission in itself...