Created
January 22, 2025 15:22
-
-
Save MadhukarMoogala/939e98dc88846634753f227d95353d26 to your computer and use it in GitHub Desktop.
AutoCAD Custom Raster Image Handling : Embedding and Display Techniques
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<ItemGroup Label="ProjectConfigurations"> | |
<ProjectConfiguration Include="Debug|x64"> | |
<Configuration>Debug</Configuration> | |
<Platform>x64</Platform> | |
</ProjectConfiguration> | |
</ItemGroup> | |
<PropertyGroup Label="Globals"> | |
<ProjectGuid>{21E65A9C-E654-4DDA-B631-4CA512141022}</ProjectGuid> | |
<!--Change this SDK Path for ObjectARX and OpenCV--> | |
<ArxSdk>D:\ArxSdks\Arx2025</ArxSdk> | |
<OpenCVSdk>D:\ArxSDKs\opencv\build</OpenCVSdk> | |
</PropertyGroup> | |
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | |
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | |
<ConfigurationType>DynamicLibrary</ConfigurationType> | |
<PlatformToolset>v143</PlatformToolset> | |
<CharacterSet>Unicode</CharacterSet> | |
</PropertyGroup> | |
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | |
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |
<TargetExt>.dbx</TargetExt> | |
</PropertyGroup> | |
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |
<ClCompile> | |
<AdditionalIncludeDirectories>$(ArxSdk)\inc;$(ArxSdk)\inc-x64;$(ArxSdk)\utils\Atil\Inc;$(ArxSdk)\utils\Atil\Inc\format_codecs;$(ArxSdk)\utils\Atil\Inc\codec_properties;$(OpenCVSdk)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |
<PreprocessorDefinitions>ACRXAPP;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
</ClCompile> | |
<Link> | |
<AdditionalLibraryDirectories>$(ArxSdk)\lib-x64;$(ArxSdk)\utils\Atil\Lib-x64;$(OpenCVSdk)\x64\vc16\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | |
<ModuleDefinitionFile>$(ArxSdk)\inc\AcRxDefault.def</ModuleDefinitionFile> | |
<AdditionalDependencies>ac1st25.lib;acad.lib;acapp.lib;acapp_crx.lib;AcCamera.lib;accore.lib;acdb25.lib;acdbmgd.lib;AcDbPointCloudObj.lib;AcDrawBridge.lib;AcFdEval.lib;AcFdUi.lib;acge25.lib;AcGeolocationObj.lib;acgeoment.lib;acgiapi.lib;acismobj25.lib;AcJsCoreStub_crx.lib;aclmvexport_crx.lib;acModelDocObj.lib;AcMPolygonObj.lib;AcPal.lib;AcPublish_crx.lib;AcSceneOE.lib;AcTc.lib;AcTcUi.lib;acui25.lib;AdApplicationFrame.lib;adui25.lib;aNav.lib;axdb.lib;rxapi.lib;userdata.lib;AdImaging.lib;AdIntImgServices.lib;opencv_world4100.lib;%(AdditionalDependencies)</AdditionalDependencies> | |
</Link> | |
</ItemDefinitionGroup> | |
<ItemGroup> | |
<ClCompile Include="main.cpp" /> | |
</ItemGroup> | |
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Windows.h" | |
#include "adesk.h" | |
#include "rxboiler.h" | |
#include "accmd.h" | |
#include "rxobject.h" | |
#include "dbents.h" | |
#include "dbmain.h" | |
#include "dbobjptr.h" | |
#include "dbapserv.h" | |
#include "tchar.h" | |
#include "rxregsvc.h" | |
#include "imgdef.h" | |
#include "imgent.h" | |
//opencv | |
#include <opencv2/opencv.hpp> | |
//ATIL | |
#include "FileWriteDescriptor.h" | |
#include "RowProviderInterface.h" | |
#include "FileSpecifier.h" | |
#include "DataModelAttributes.h" | |
#include "DataModel.h" | |
#include "Image.h" | |
#include "RgbColor.h" | |
#include "ImageFilter.h" | |
#include <JfifFormatCodec.h> | |
#include <PngFormatCodec.h> | |
#include <TiffFormatCodec.h> | |
#include <BmpFormatCodec.h> | |
#include <TiffCustomProperties.h> | |
#include <PngCustomProperties.h> | |
#include <FileReadDescriptor.h> | |
#include <RgbPaletteModel.h> | |
#include "AcTc.h" | |
/// <summary> | |
/// This project demonstrates two approaches to embedding and displaying images in AutoCAD drawings, | |
/// tailored to different use cases and requirements: | |
/// | |
/// 1. TstRasterImageDef Class: | |
/// - A custom implementation of the AcDbRasterImageDef class. | |
/// - Provides functionality to embed an image from a specified file path into the drawing | |
/// using the ATIL image library. | |
/// - Handles image data through the Atil::Image class, with support for serialization and | |
/// deserialization of image data in DWG files. | |
/// - NOTE: This approach does not generate proxy graphics. If the parent DBX application is | |
/// unloaded, the embedded image will not be displayed. | |
/// | |
/// 2. AcDbEntity-Based Image Display: | |
/// - Implements a lightweight AcDbEntity to display images using AcGiWorldDraw and | |
/// AcGiViewportDraw. | |
/// - Images are read from bitmap files using OpenCV and rendered as AcGiImageBGRA32, | |
/// scaled to fit the viewport. | |
/// - Stores the image directly in the drawing's database, generating proxy graphics to | |
/// ensure the image remains visible even if the parent DBX application is unloaded. | |
/// | |
/// Together, these implementations highlight the flexibility of AutoCAD's API for handling | |
/// raster images, whether for performance-critical scenarios requiring lightweight entities | |
/// or for robust, persistent image embedding using ATIL. This project serves as a reference | |
/// for developers seeking to extend AutoCAD's capabilities with custom image-handling functionality. | |
/// | |
/// NOTE: To use this project, ensure that the OpenCV and ATIL libraries are correctly linked. | |
/// NOTE: This project is intended for educational purposes only and may require additional testing and validation. | |
/// | |
/// </summary> | |
/// <summary> | |
/// The function appends the entity to the MODEL_SPACE block table record and closes the entity as it's now part of the database. | |
/// </summary> | |
/// <param name="ent"></param> | |
/// <param name="objId"></param> | |
/// <returns>Acad::eOk</returns> | |
Acad::ErrorStatus postToDatabase(AcDbEntity* ent, AcDbObjectId& objId) | |
{ | |
Acad::ErrorStatus es; | |
// Use AcDbBlockTablePointer for automatic management of AcDbBlockTable | |
AcDbBlockTablePointer pBlockTable(acdbHostApplicationServices()->workingDatabase(), AcDb::kForRead); | |
if ((es = pBlockTable.openStatus()) != Acad::eOk) { | |
return es; | |
} | |
// Use AcDbBlockTableRecordPointer for automatic management of AcDbBlockTableRecord | |
AcDbBlockTableRecordPointer pSpaceRecord(ACDB_MODEL_SPACE, acdbHostApplicationServices()->workingDatabase(), AcDb::kForWrite); | |
if ((es = pSpaceRecord.openStatus()) != Acad::eOk) { | |
return es; | |
} | |
// Append the entity to the MODEL_SPACE block table record | |
if ((es = pSpaceRecord->appendAcDbEntity(objId, ent)) != Acad::eOk) { | |
return es; | |
} | |
// Close the entity as it's now part of the database | |
if ((es = ent->close()) != Acad::eOk) { | |
return es; | |
} | |
return Acad::eOk; | |
} | |
/// <summary> | |
/// Utility function to read Pixel data from bitmap using OpenCV. | |
/// </summary> | |
struct ImageData { | |
std::unique_ptr<AcGiPixelBGRA32[]> pixelData; | |
Adesk::UInt32 width{ 100 }; | |
Adesk::UInt32 height{ 200 }; | |
size_t size() const { return static_cast<size_t>(width) * height; } | |
bool isInitialized() const { return pixelData != nullptr; } | |
void initialize(const std::string& imagePath) { | |
cv::Mat img = cv::imread(imagePath, cv::IMREAD_COLOR); // Read in color (BGR) | |
if (img.empty()) { | |
acutPrintf(_T("Error: Could not open or find the image!\n")); | |
pixelData.reset(); | |
return; | |
} | |
// Flip the image vertically | |
cv::flip(img, img, 0); | |
width = img.cols; | |
height = img.rows; | |
pixelData = std::make_unique<AcGiPixelBGRA32[]>(size()); | |
for (size_t i = 0; i < size(); ++i) { | |
int y = i / width, x = i % width; | |
if (y < img.rows && x < img.cols) { | |
cv::Vec3b color = img.at<cv::Vec3b>(y, x); | |
pixelData[i].setRGBA(color[2], color[1], color[0], 255); // Convert BGR to RGBA | |
} | |
} | |
} | |
}; | |
//AcDbEntity-Based Image Display | |
/// <summary> | |
/// This class is a lightweight AcDbEntity designed to display an image within a drawing using | |
/// AcGiWorldDraw and AcGiViewportDraw. The image is read from a bitmap file using OpenCV and | |
/// rendered as an AcGiImageBGRA32, scaled to fit the viewport. | |
/// The image is embedded in the drawing's database, and proxy graphics are generated to ensure | |
/// the image remains visible even if the parent DBX application is unloaded. | |
/// </summary> | |
class ImageWorldDraw : public AcDbEntity { | |
public: | |
ACRX_DECLARE_MEMBERS(ImageWorldDraw); | |
ImageWorldDraw(); | |
ImageWorldDraw(std::unique_ptr<ImageData> imageData); | |
~ImageWorldDraw(); | |
virtual Adesk::Boolean subWorldDraw(AcGiWorldDraw* mode) override; | |
virtual void subViewportDraw(AcGiViewportDraw* mode) override; | |
virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* filer) override; | |
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* filer) const override; | |
virtual Acad::ErrorStatus subTransformBy(const AcGeMatrix3d& xform) override; | |
virtual void saveAs(AcGiWorldDraw* mode, AcDb::SaveType st) override; | |
private: | |
std::unique_ptr<ImageData> m_imageData; | |
double scaleFactor = 1.0; | |
}; | |
ACRX_DXF_DEFINE_MEMBERS( | |
ImageWorldDraw, | |
AcDbEntity, | |
AcDb::kDHL_CURRENT, | |
AcDb::kMReleaseCurrent, | |
0, | |
IMAGE_WORLD_DRAW, | |
ImgSamp); | |
ImageWorldDraw::ImageWorldDraw() = default; | |
ImageWorldDraw::ImageWorldDraw(std::unique_ptr<ImageData> imageData) | |
: m_imageData(std::move(imageData)) { | |
} | |
ImageWorldDraw::~ImageWorldDraw() = default; | |
Adesk::Boolean ImageWorldDraw::subWorldDraw(AcGiWorldDraw* mode) { | |
return Adesk::kFalse; | |
} | |
void ImageWorldDraw::subViewportDraw(AcGiViewportDraw* mode) | |
{ | |
if (!m_imageData || !m_imageData->isInitialized()) | |
return; | |
// Get the current viewport | |
AcGePoint2d lower_left, upper_right; | |
mode->viewport().getViewportDcCorners(lower_left, upper_right); | |
double xsize = upper_right.x - lower_left.x; | |
double ysize = upper_right.y - lower_left.y; | |
xsize /= 10.0; | |
ysize /= 10.0; | |
scaleFactor = xsize / ysize; | |
std::unique_ptr<AcGiPixelBGRA32[]> scaledPixelData; | |
AcGiImageBGRA32* imageSource = nullptr; | |
if (scaleFactor != 1.0) { | |
Adesk::UInt32 scaledWidth = static_cast<Adesk::UInt32>(m_imageData->width * scaleFactor); | |
Adesk::UInt32 scaledHeight = static_cast<Adesk::UInt32>(m_imageData->height * scaleFactor); | |
// Ensure minimum dimensions | |
scaledWidth = std::max(scaledWidth, (Adesk::UInt32)1); | |
scaledHeight = std::max(scaledHeight, (Adesk::UInt32)1); | |
// Create OpenCV matrix with the correct format | |
cv::Mat srcMat(m_imageData->height, m_imageData->width, CV_8UC3); | |
// Copy pixel data to OpenCV matrix | |
for (size_t i = 0; i < m_imageData->size(); ++i) { | |
int y = i / m_imageData->width; | |
int x = i % m_imageData->width; | |
const auto& pixel = m_imageData->pixelData[i]; | |
srcMat.at<cv::Vec3b>(y, x)[0] = pixel.blue(); | |
srcMat.at<cv::Vec3b>(y, x)[1] = pixel.green(); | |
srcMat.at<cv::Vec3b>(y, x)[2] = pixel.red(); | |
} | |
// Resize | |
cv::Mat dstMat; | |
cv::resize(srcMat, dstMat, cv::Size(scaledWidth, scaledHeight), 0, 0, cv::INTER_LINEAR); | |
// Create scaled pixel data | |
scaledPixelData = std::make_unique<AcGiPixelBGRA32[]>(scaledWidth * scaledHeight); | |
// Copy back to BGRA format | |
for (size_t i = 0; i < static_cast<size_t>(scaledWidth) * scaledHeight; ++i) { | |
int y = i / scaledWidth; | |
int x = i % scaledWidth; | |
const auto& color = dstMat.at<cv::Vec3b>(y, x); | |
scaledPixelData[i].setRGBA(color[2], color[1], color[0], 255); // Set alpha to 255 | |
} | |
imageSource = new AcGiImageBGRA32(scaledWidth, scaledHeight, scaledPixelData.get()); | |
} | |
else { | |
imageSource = new AcGiImageBGRA32(m_imageData->width, m_imageData->height, m_imageData->pixelData.get()); | |
} | |
if (imageSource) { | |
AcGePoint3d position(4.0, 5.0, 6.0); | |
AcGeVector3d u(std::abs(scaleFactor) * m_imageData->width, 0.0, 0.0); | |
AcGeVector3d v(0.0, std::abs(scaleFactor) * m_imageData->height, 0.0); | |
mode->geometry().image(*imageSource, position, u, v, AcGiGeometry::kTransparencyOff); | |
delete imageSource; | |
} | |
} | |
Acad::ErrorStatus ImageWorldDraw::dwgInFields(AcDbDwgFiler* filer) { | |
assertWriteEnabled(); | |
Acad::ErrorStatus es = AcDbEntity::dwgInFields(filer); | |
if (es != Acad::eOk) | |
return es; | |
m_imageData = std::make_unique<ImageData>(); | |
filer->readUInt32(&m_imageData->width); | |
filer->readUInt32(&m_imageData->height); | |
const size_t imageSize = m_imageData->size(); | |
if (imageSize > 0) { | |
m_imageData->pixelData = std::make_unique<AcGiPixelBGRA32[]>(imageSize); | |
for (size_t i = 0; i < imageSize; ++i) { | |
filer->readBytes(&m_imageData->pixelData[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; | |
filer->writeUInt32(m_imageData->width); | |
filer->writeUInt32(m_imageData->height); | |
const size_t imageSize = m_imageData->size(); | |
if (imageSize > 0 && m_imageData->pixelData) { | |
for (size_t i = 0; i < imageSize; ++i) { | |
filer->writeBytes(&m_imageData->pixelData[i], sizeof(AcGiPixelBGRA32)); | |
} | |
} | |
return filer->filerStatus(); | |
} | |
Acad::ErrorStatus ImageWorldDraw::subTransformBy(const AcGeMatrix3d& xform) { | |
if (!m_imageData || !m_imageData->isInitialized()) | |
return Acad::eOk; | |
scaleFactor = xform.scale(); | |
return Acad::eOk; | |
} | |
void ImageWorldDraw::saveAs(AcGiWorldDraw* mode, AcDb::SaveType st) { | |
if (!m_imageData || !m_imageData->isInitialized()) | |
return; | |
AcGiImageBGRA32 imageSource(m_imageData->width, m_imageData->height, m_imageData->pixelData.get()); | |
AcGePoint3d position(4.0, 5.0, 6.0); | |
AcGeVector3d u(scaleFactor * m_imageData->width, 0.0, 0.0); | |
AcGeVector3d v(0.0, scaleFactor * m_imageData->height, 0.0); | |
AcGiGeometry::TransparencyMode transparencyMode = AcGiGeometry::kTransparencyOff; | |
mode->rawGeometry()->image(imageSource, position, u, v, transparencyMode); | |
} | |
/// <summary> | |
/// The TstRasterImageDef class is a custom implementation of the AcDbRasterImageDef class. | |
/// It provides methods to read and write data to and from DWG files, along with the capability | |
/// to embed an image from a specified file path into the object using the ATIL image library. | |
/// The class uses the Atil::Image class for image handling and supports serialization and | |
/// deserialization of image data within a DWG file. | |
/// NOTE: This implementation embeds the image in the drawing but does not generate proxy graphics. | |
/// If the DBX application is unloaded, the image will not be displayed. | |
/// </summary> | |
class TstRasterImageDef: public AcDbRasterImageDef { | |
protected: | |
static Adesk::UInt32 kCurrentVersionNumber; | |
public: | |
ACRX_DECLARE_MEMBERS(TstRasterImageDef); | |
TstRasterImageDef(); | |
virtual ~TstRasterImageDef(); | |
virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* filer) override; | |
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* filer) const override; | |
Acad::ErrorStatus setEmbeddedImage(const ACHAR* imageFilePath); | |
private: | |
Atil::Image* m_pAtilImage; | |
}; | |
Adesk::UInt32 TstRasterImageDef::kCurrentVersionNumber = 1; | |
ACRX_DXF_DEFINE_MEMBERS( | |
TstRasterImageDef, AcDbRasterImageDef, | |
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent, | |
0, TST_RASTER_IMAGE_DEF, "TstRasterImageApp") | |
TstRasterImageDef::TstRasterImageDef() : m_pAtilImage(nullptr) {} | |
TstRasterImageDef::~TstRasterImageDef() { | |
if (m_pAtilImage) { | |
delete m_pAtilImage; | |
m_pAtilImage = nullptr; | |
} | |
} | |
Acad::ErrorStatus TstRasterImageDef::dwgOutFields(AcDbDwgFiler* pFiler) const { | |
assertReadEnabled(); | |
//----- Save parent class information first. | |
Acad::ErrorStatus es = AcDbRasterImageDef::dwgOutFields(pFiler); | |
if (es != Acad::eOk) | |
return (es); | |
//----- Object version number needs to be saved first | |
if ((es = pFiler->writeUInt32(TstRasterImageDef::kCurrentVersionNumber)) != Acad::eOk) | |
return (es); | |
if (m_pAtilImage) | |
{ | |
Atil::Size size = m_pAtilImage->size(); | |
Adesk::Int32 width = size.width; | |
Adesk::Int32 height = size.height; | |
pFiler->writeInt32(width); | |
pFiler->writeInt32(height); | |
// Write the image data on to the Atil image | |
// using an Image Context | |
Atil::Offset upperLeft(0, 0); | |
Atil::ImageContext* pImgContext = m_pAtilImage->createContext( | |
Atil::ImageContext::kRead, | |
size, | |
upperLeft | |
); | |
if (pImgContext != NULL) | |
{ | |
for (int xf = 0; xf < width; xf++) | |
{ | |
for (int yf = 0; yf < height; yf++) | |
{ | |
Atil::RgbColor p; | |
p = pImgContext->get32(xf, yf); | |
pFiler->writeInt32(p.packed); | |
} | |
} | |
} | |
pImgContext->flush(); | |
delete pImgContext; | |
} | |
return (pFiler->filerStatus()); | |
} | |
Acad::ErrorStatus TstRasterImageDef::dwgInFields(AcDbDwgFiler* pFiler) { | |
assertWriteEnabled(); | |
//----- Read parent class information first. | |
Acad::ErrorStatus es = AcDbRasterImageDef::dwgInFields(pFiler); | |
if (es != Acad::eOk) | |
return (es); | |
//----- Object version number needs to be read first | |
Adesk::UInt32 version = 0; | |
if ((es = pFiler->readUInt32(&version)) != Acad::eOk) | |
return (es); | |
if (version > TstRasterImageDef::kCurrentVersionNumber) | |
return (Acad::eMakeMeProxy); | |
Adesk::Int32 width = 0; | |
Adesk::Int32 height = 0; | |
pFiler->readInt32(&width); | |
pFiler->readInt32(&height); | |
// Create an Atil::Image. | |
// The AcDbRasterImageDef::setImage requires it | |
Atil::ImagePixel initialImage; | |
initialImage.setToZero(); | |
initialImage.type = Atil::DataModelAttributes::kBgra; | |
initialImage.value.rgba = 0xff000000; | |
Atil::Size size(width, height); | |
const Atil::RgbModel* pDm = new Atil::RgbModel( | |
Atil::RgbModelAttributes::k4Channels, | |
Atil::DataModelAttributes::kBlueGreenRedAlpha); | |
if (m_pAtilImage != NULL) | |
{ | |
delete m_pAtilImage; | |
m_pAtilImage = NULL; | |
} | |
m_pAtilImage = new Atil::Image(size, pDm, initialImage); | |
// Write the image data on to the Atil image | |
// using an Image Context | |
Atil::Offset upperLeft(0, 0); | |
Atil::ImageContext* pImgContext = m_pAtilImage->createContext( | |
Atil::ImageContext::kWrite, | |
size, | |
upperLeft | |
); | |
if (pImgContext != NULL) | |
{ | |
for (int xf = 0; xf < width; xf++) | |
{ | |
for (int yf = 0; yf < height; yf++) | |
{ | |
Adesk::Int32 value; | |
pFiler->readInt32(&value); | |
pImgContext->put32(xf, yf, value); | |
} | |
} | |
} | |
pImgContext->flush(); | |
delete pImgContext; | |
setImage(m_pAtilImage, NULL); | |
return (pFiler->filerStatus()); | |
} | |
Acad::ErrorStatus TstRasterImageDef::setEmbeddedImage(const ACHAR* imageFilePath) { | |
assertWriteEnabled(); | |
if (!imageFilePath || *imageFilePath == '\0') { | |
return Acad::eInvalidInput; | |
} | |
AcString imagePath(imageFilePath); | |
// Load the image to get access to its image buffer | |
AcTcImage tc; | |
if (!tc.Load(imagePath)) { | |
return Acad::eFileNotFound; | |
} | |
HBITMAP bmp = 0; | |
tc.GetHBITMAP(RGB(0xff, 0xff, 0xff), bmp); | |
if (!bmp) { | |
return Acad::eFileNotFound; | |
} | |
BITMAP bmpInfo = { 0 }; | |
if (!GetObject(bmp, sizeof(BITMAP), &bmpInfo)) { | |
DeleteObject(bmp); | |
return Acad::eInvalidInput; | |
} | |
HDC hdcScr = GetDC(NULL); | |
if (!hdcScr) { | |
DeleteObject(bmp); | |
return Acad::eCreateFailed; | |
} | |
HDC hdcMem = CreateCompatibleDC(hdcScr); | |
if (!hdcMem) { | |
ReleaseDC(NULL, hdcScr); | |
DeleteObject(bmp); | |
return Acad::eCreateFailed; | |
} | |
SelectObject(hdcMem, bmp); | |
// Create an Atil::Image for AcDbRasterImageDef::setImage | |
Atil::ImagePixel initialImage; | |
initialImage.setToZero(); | |
initialImage.type = Atil::DataModelAttributes::kBgra; | |
initialImage.value.rgba = 0xff000000; | |
Atil::Size size(bmpInfo.bmWidth, bmpInfo.bmHeight); | |
std::unique_ptr<const Atil::RgbModel> pDm(new Atil::RgbModel( | |
Atil::RgbModelAttributes::k4Channels, | |
Atil::DataModelAttributes::kBlueGreenRedAlpha)); | |
if (m_pAtilImage) { | |
delete m_pAtilImage; | |
m_pAtilImage = nullptr; | |
} | |
m_pAtilImage = new Atil::Image(size, pDm.get(), initialImage); | |
// Write image data to Atil::Image using an ImageContext | |
Atil::Offset upperLeft(0, 0); | |
std::unique_ptr<Atil::ImageContext> pImgContext( | |
m_pAtilImage->createContext(Atil::ImageContext::kWrite, size, upperLeft)); | |
if (!pImgContext) { | |
DeleteDC(hdcMem); | |
ReleaseDC(NULL, hdcScr); | |
DeleteObject(bmp); | |
return Acad::eInvalidInput; | |
} | |
for (int x = 0; x < bmpInfo.bmWidth; ++x) { | |
for (int y = 0; y < bmpInfo.bmHeight; ++y) { | |
COLORREF pixelColor = GetPixel(hdcMem, x, y); | |
BYTE r = GetRValue(pixelColor); | |
BYTE g = GetGValue(pixelColor); | |
BYTE b = GetBValue(pixelColor); | |
BYTE a = (r != 0xff || g != 0xff || b != 0xff) ? 0xff : 0x00; | |
Atil::RgbColor pixel; | |
pixel.set(r, g, b, a); | |
pImgContext->put32(x, y, pixel); | |
} | |
} | |
pImgContext->flush(); | |
if (!m_pAtilImage->isValid()) { | |
DeleteDC(hdcMem); | |
ReleaseDC(NULL, hdcScr); | |
DeleteObject(bmp); | |
return Acad::eInvalidInput; | |
} | |
// Cleanup | |
DeleteDC(hdcMem); | |
ReleaseDC(NULL, hdcScr); | |
DeleteObject(bmp); | |
return setImage(m_pAtilImage, nullptr); | |
} | |
/// <summary> | |
/// Command method to embed an image in the drawing database. | |
/// </summary> | |
void embedImage() { | |
auto imageData = std::make_unique<ImageData>(); | |
imageData->initialize("D:\\Work\\Projects\\2025\\imgSamp\\1234.bmp"); | |
if (!imageData->isInitialized()) { | |
acutPrintf(_T("\nFailed to initialize image data.")); | |
return; | |
} | |
auto* pImageWorldDraw = new ImageWorldDraw(std::move(imageData)); | |
AcDbObjectId imageOID; | |
Acad::ErrorStatus es = postToDatabase(pImageWorldDraw, imageOID); | |
if (es != Acad::eOk) { | |
acutPrintf(_T("\nFailed to post the image to the database.")); | |
delete pImageWorldDraw; | |
} | |
} | |
static Acad::ErrorStatus InsertImageInDwg(AcDbRasterImageDef* pImageDef) | |
{ | |
Acad::ErrorStatus es; | |
if (!pImageDef->isLoaded()) { | |
es = pImageDef->load(); | |
if (es != Acad::eOk) | |
return es; | |
} | |
AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase(); | |
// Get or create the image dictionary | |
AcDbObjectId dictID = AcDbRasterImageDef::imageDictionary(pDB); | |
if (dictID == AcDbObjectId::kNull) { | |
es = AcDbRasterImageDef::createImageDictionary(pDB, dictID); | |
if (es != Acad::eOk) | |
return es; | |
} | |
AcDbDictionary* pDict; | |
es = acdbOpenObject(pDict, dictID, AcDb::kForWrite); | |
if (es != Acad::eOk) | |
return es; | |
const ACHAR* DICT_NAME = ACRX_T("RASTER_USING_BUFFER"); | |
AcDbObjectId objID = AcDbObjectId::kNull; | |
if (!pDict->has(DICT_NAME)) { | |
es = pDict->setAt(DICT_NAME, pImageDef, objID); | |
if (es != Acad::eOk) { | |
pDict->close(); | |
return es; | |
} | |
} | |
else { | |
es = pDict->getAt(DICT_NAME, (AcDbObject*&)pImageDef, AcDb::kForWrite); | |
if (es != Acad::eOk) { | |
pDict->close(); | |
return es; | |
} | |
objID = pImageDef->objectId(); | |
} | |
pDict->close(); | |
pImageDef->close(); | |
// Create and configure a raster image entity | |
AcDbRasterImage* pImage = new AcDbRasterImage(); | |
es = pImage->setImageDefId(objID); | |
if (es != Acad::eOk) { | |
delete pImage; | |
return es; | |
} | |
es = postToDatabase(pImage, objID); | |
if (es != Acad::eOk) { | |
delete pImage; | |
return es; | |
} | |
// Set up the reactor for the raster image | |
AcDbObjectPointer<AcDbRasterImageDefReactor> rasterImageDefReactor; | |
rasterImageDefReactor.create(); | |
es = rasterImageDefReactor->setOwnerId(pImage->objectId()); | |
if (es == Acad::eOk) { | |
AcDbObjectId defReactorId; | |
es = pDB->addAcDbObject(defReactorId, rasterImageDefReactor.object()); | |
if (es == Acad::eOk) { | |
pImage->setReactorId(defReactorId); | |
AcDbObjectPointer<AcDbRasterImageDef> rasterImagedef(pImage->imageDefId(), AcDb::kForWrite); | |
if (rasterImagedef.openStatus() == Acad::eOk) { | |
rasterImagedef->addPersistentReactor(defReactorId); | |
} | |
} | |
} | |
return Acad::eOk; | |
} | |
/// <summary> | |
/// command method to insert a raster image in the drawing database. | |
/// </summary> | |
void rasterImage() { | |
TstRasterImageDef* pImageDef = new TstRasterImageDef(); | |
Acad::ErrorStatus es = pImageDef->setEmbeddedImage(L"D:\\Work\\Projects\\2025\\imgSamp\\1234.bmp"); | |
if (es != Acad::eOk) { | |
delete pImageDef; | |
return; | |
} | |
es = InsertImageInDwg(pImageDef); | |
if (es != Acad::eOk) { | |
delete pImageDef; | |
} | |
} | |
void initApp() | |
{ | |
acrxDynamicLinker->loadModule(L"acismobj25.dbx", true); | |
ImageWorldDraw::rxInit(); | |
TstRasterImageDef::rxInit(); | |
acedRegCmds->addCommand(_T("IMGSAMP_COMMANDS"), _T("IMAGETEST"), _T("IMAGETEST"), | |
ACRX_CMD_MODAL, &embedImage); | |
acedRegCmds->addCommand(_T("IMGSAMP_COMMANDS"), _T("RASTERIMAGE"), _T("RASTERIMAGE"), ACRX_CMD_MODAL, &rasterImage); | |
acrxBuildClassHierarchy(); | |
} | |
void unloadApp() | |
{ | |
acedRegCmds->removeGroup( _T("IMGSAMP_COMMANDS") ); | |
} | |
extern "C" AcRx::AppRetCode acrxEntryPoint( AcRx::AppMsgCode msg, void* pkt) | |
{ | |
switch( msg ) { | |
case AcRx::kInitAppMsg: | |
//An ARX application is locked to memory by default. | |
//You must make the following function call to unlock it. | |
acrxDynamicLinker->unlockApplication(pkt); | |
//support MDI | |
acrxDynamicLinker->registerAppMDIAware(pkt); | |
initApp(); | |
break; | |
case AcRx::kUnloadAppMsg: | |
deleteAcRxClass(ImageWorldDraw::desc()); | |
unloadApp(); | |
break; | |
default: | |
break; | |
} | |
return AcRx::kRetOK; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment