ASFUNCTIONBODY(BitmapData,_constructor) { uint32_t width; uint32_t height; bool transparent; uint32_t fillColor; BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); ARG_UNPACK(width, 0)(height, 0)(transparent, true)(fillColor, 0xFFFFFFFF); ASObject::_constructor(obj,NULL,0); //If the bitmap is already initialized, just return if(width==0 || height==0 || !th->data.empty()) return NULL; uint32_t *pixels=new uint32_t[width*height]; uint32_t c=GUINT32_TO_BE(fillColor); // fromRGB expects big endian data if(!transparent) { uint8_t *alpha=reinterpret_cast<uint8_t *>(&c); *alpha=0xFF; } for(uint32_t i=0; i<width*height; i++) pixels[i]=c; th->fromRGB(reinterpret_cast<uint8_t *>(pixels), width, height, ARGB32); th->transparent=transparent; return NULL; }
void Image::desaturate() { if (isARGB() || isRGB()) { const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); if (isARGB()) { for (int y = 0; y < destData.height; ++y) { uint8* p = destData.getLinePointer (y); for (int x = 0; x < destData.width; ++x) { ((PixelARGB*) p)->desaturate(); p += destData.pixelStride; } } } else { for (int y = 0; y < destData.height; ++y) { uint8* p = destData.getLinePointer (y); for (int x = 0; x < destData.width; ++x) { ((PixelRGB*) p)->desaturate(); p += destData.pixelStride; } } } } }
ASFUNCTIONBODY(BitmapData,hitTest) { BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); _NR<Point> firstPoint; uint32_t firstAlphaThreshold; _NR<ASObject> secondObject; _NR<Point> secondBitmapDataPoint; uint32_t secondAlphaThreshold; ARG_UNPACK (firstPoint) (firstAlphaThreshold) (secondObject) (secondBitmapDataPoint, NullRef) (secondAlphaThreshold,1); if(!secondObject->getClass() || !secondObject->getClass()->isSubClass(Class<Point>::getClass())) throw Class<TypeError>::getInstanceS("Error #1034: Wrong type"); if(!secondBitmapDataPoint.isNull() || secondAlphaThreshold!=1) LOG(LOG_NOT_IMPLEMENTED,"BitmapData.hitTest does not expect some parameters"); Point* secondPoint = secondObject->as<Point>(); uint32_t pix=th->getPixelPriv(secondPoint->getX()-firstPoint->getX(), secondPoint->getY()-firstPoint->getY()); if((pix>>24)>=firstAlphaThreshold) return abstract_b(true); else return abstract_b(false);
void Image::setPixelAt (const int x, const int y, const Colour& colour) { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { const BitmapData destData (*this, x, y, 1, 1, BitmapData::writeOnly); destData.setPixelColour (0, 0, colour); } }
//============================================================================== Colour Image::getPixelAt (const int x, const int y) const { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { const BitmapData srcData (*this, x, y, 1, 1); return srcData.getPixelColour (0, 0); } return Colour(); }
ASFUNCTIONBODY(BitmapData,dispose) { BitmapData* th = obj->as<BitmapData>(); th->width = 0; th->height = 0; th->data.clear(); th->data.shrink_to_fit(); th->disposed = true; th->notifyUsers(); return NULL; }
void or_bitmapdata_dimensions(ORBitmapDataRef bitmapdata, uint32_t *x, uint32_t *y) { BitmapData *t = reinterpret_cast<BitmapData *>(bitmapdata); if (x != NULL) { *x = t->width(); } if (y != NULL) { *y = t->height(); } }
ASFUNCTIONBODY(BitmapData,setPixel32) { BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); uint32_t x; uint32_t y; uint32_t color; ARG_UNPACK(x)(y)(color); th->setPixelPriv(x, y, color, th->transparent); return NULL; }
ASFUNCTIONBODY(BitmapData,getPixel32) { BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); int32_t x; int32_t y; ARG_UNPACK(x)(y); uint32_t pix=th->getPixelPriv(x, y); return abstract_ui(pix); }
void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const { if (hasAlphaChannel()) { const uint8 threshold = (uint8) jlimit (0, 255, roundToInt (alphaThreshold * 255.0f)); SparseSet<int> pixelsOnRow; const BitmapData srcData (*this, 0, 0, getWidth(), getHeight()); for (int y = 0; y < srcData.height; ++y) { pixelsOnRow.clear(); const uint8* lineData = srcData.getLinePointer (y); if (isARGB()) { for (int x = 0; x < srcData.width; ++x) { if (((const PixelARGB*) lineData)->getAlpha() >= threshold) pixelsOnRow.addRange (Range<int> (x, x + 1)); lineData += srcData.pixelStride; } } else { for (int x = 0; x < srcData.width; ++x) { if (*lineData >= threshold) pixelsOnRow.addRange (Range<int> (x, x + 1)); lineData += srcData.pixelStride; } } for (int i = 0; i < pixelsOnRow.getNumRanges(); ++i) { const Range<int> range (pixelsOnRow.getRange (i)); result.add (Rectangle<int> (range.getStart(), y, range.getLength(), 1)); } result.consolidate(); } } else { result.add (0, 0, getWidth(), getHeight()); } }
ASFUNCTIONBODY(BitmapData,draw) { BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); _NR<ASObject> drawable; _NR<Matrix> matrix; _NR<ColorTransform> ctransform; _NR<ASString> blendMode; _NR<Rectangle> clipRect; bool smoothing; ARG_UNPACK (drawable) (matrix, NullRef) (ctransform, NullRef) (blendMode, NullRef) (clipRect, NullRef) (smoothing, false); if(!drawable->getClass() || !drawable->getClass()->isSubClass(InterfaceClass<IBitmapDrawable>::getClass()) ) throw Class<TypeError>::getInstanceS("Error #1034: Wrong type"); if(!ctransform.isNull() || !blendMode.isNull() || !clipRect.isNull() || smoothing) LOG(LOG_NOT_IMPLEMENTED,"BitmapData.draw does not support many parameters"); if(drawable->is<BitmapData>()) { BitmapData* data=drawable->as<BitmapData>(); //Compute the initial matrix, if any MATRIX initialMatrix; if(!matrix.isNull()) initialMatrix=matrix->getMATRIX(); CairoRenderContext ctxt(th->getData(), th->width, th->height); //Blit the data while transforming it ctxt.transformedBlit(initialMatrix, data->getData(), data->getWidth(), data->getHeight(), CairoRenderContext::FILTER_NONE); } else if(drawable->is<DisplayObject>()) { DisplayObject* d=drawable->as<DisplayObject>(); //Compute the initial matrix, if any MATRIX initialMatrix; if(!matrix.isNull()) initialMatrix=matrix->getMATRIX(); th->drawDisplayObject(d, initialMatrix); } else LOG(LOG_NOT_IMPLEMENTED,"BitmapData.draw does not support " << drawable->toDebugString()); th->notifyUsers(); return NULL; }
void Bitmap::setTexture(TextureBase *texturebase) { BitmapData *originalbitmapdata = bitmapdata_; TextureBase *originaltexturebase = texturebase_; bitmapdata_ = NULL; texturebase_ = texturebase; texturebase_->ref(); setAnchorPoint(anchorx_, anchory_); // here setAnchorPoint updates geometry and bounds if (originalbitmapdata) originalbitmapdata->unref(); if (originaltexturebase) originaltexturebase->unref(); }
void Image::setPixelData (int x, int y, int w, int h, const uint8* const sourcePixelData, const int sourceLineStride) { jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= getWidth() && y + h <= getHeight()); if (Rectangle<int>::intersectRectangles (x, y, w, h, 0, 0, getWidth(), getHeight())) { const BitmapData dest (*this, x, y, w, h, BitmapData::writeOnly); for (int i = 0; i < h; ++i) { memcpy (dest.getLinePointer(i), sourcePixelData + sourceLineStride * i, w * dest.pixelStride); } } }
Image Image::convertedToFormat (PixelFormat newFormat) const { if (image == nullptr || newFormat == image->pixelFormat) return *this; const int w = image->width, h = image->height; const ScopedPointer<ImageType> type (image->createType()); Image newImage (type->create (newFormat, w, h, false)); if (newFormat == SingleChannel) { if (! hasAlphaChannel()) { newImage.clear (getBounds(), Colours::black); } else { const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); const BitmapData srcData (*this, 0, 0, w, h); for (int y = 0; y < h; ++y) { const PixelARGB* const src = (const PixelARGB*) srcData.getLinePointer (y); uint8* const dst = destData.getLinePointer (y); for (int x = 0; x < w; ++x) dst[x] = src[x].getAlpha(); } } } else if (image->pixelFormat == SingleChannel && newFormat == Image::ARGB) { const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); const BitmapData srcData (*this, 0, 0, w, h); for (int y = 0; y < h; ++y) { const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y); PixelARGB* const dst = (PixelARGB*) destData.getLinePointer (y); for (int x = 0; x < w; ++x) dst[x].set (src[x]); } } else { if (hasAlphaChannel()) newImage.clear (getBounds()); Graphics g (newImage); g.drawImageAt (*this, 0, 0); } return newImage; }
Image Image::convertedToFormat (PixelFormat newFormat) const { if (image == nullptr || newFormat == image->format) return *this; const int w = image->width, h = image->height; Image newImage (newFormat, w, h, false, image->getType()); if (newFormat == SingleChannel) { if (! hasAlphaChannel()) { newImage.clear (getBounds(), Colours::black); } else { const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); const BitmapData srcData (*this, 0, 0, w, h); for (int y = 0; y < h; ++y) { const PixelARGB* src = (const PixelARGB*) srcData.getLinePointer(y); uint8* dst = destData.getLinePointer (y); for (int x = w; --x >= 0;) { *dst++ = src->getAlpha(); ++src; } } } } else { if (hasAlphaChannel()) newImage.clear (getBounds()); Graphics g (newImage); g.drawImageAt (*this, 0, 0); } return newImage; }
void Image::multiplyAllAlphas (const float amountToMultiplyBy) { if (hasAlphaChannel()) { const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); if (isARGB()) { for (int y = 0; y < destData.height; ++y) { uint8* p = destData.getLinePointer (y); for (int x = 0; x < destData.width; ++x) { ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy); p += destData.pixelStride; } } } else { for (int y = 0; y < destData.height; ++y) { uint8* p = destData.getLinePointer (y); for (int x = 0; x < destData.width; ++x) { *p = (uint8) (*p * amountToMultiplyBy); p += destData.pixelStride; } } } } else { jassertfalse; // can't do this without an alpha-channel! } }
//-------------------------------------------------------------------------------- BitmapData* LoadJPEG(const Path& in_file) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; JSAMPARRAY buffer; FOPEN(&infile, in_file.GetData(), L("rb")); if (infile == nullptr) { Assert(false); return nullptr; } cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(infile); return nullptr; } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, true); jpeg_start_decompress(&cinfo); BufferFormat eFormat = BufferFormat::INVALID_FORMAT; switch (cinfo.out_color_space) { case JCS_EXT_ABGR: eFormat = BufferFormat::RGBA_U32; break; case JCS_EXT_BGRA: eFormat = BufferFormat::ARGB_U32; break; case JCS_EXT_RGBA: eFormat = BufferFormat::ABGR_U32; break; case JCS_RGB: eFormat = BufferFormat::BGR_U24; break; case JCS_EXT_BGR: eFormat = BufferFormat::RGB_U24; break; //case JCS_RGB565: eFormat = BufferFormat::R5G6B5_U16; break; case JCS_GRAYSCALE: eFormat = BufferFormat::A_U8; break; default: Assert(false); return nullptr; } BitmapData* pData = new BitmapData(cinfo.output_width, cinfo.output_height, eFormat); buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pData->GetBufferPitch(), 1); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy((u8*)&(pData->GetBuffer()[(cinfo.output_scanline-1) * pData->GetBufferPitch()]), buffer[0], pData->GetBufferPitch()); } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); Assert(jerr.pub.num_warnings == 0); return pData; }
void Image::moveImageSection (int dx, int dy, int sx, int sy, int w, int h) { if (dx < 0) { w += dx; sx -= dx; dx = 0; } if (dy < 0) { h += dy; sy -= dy; dy = 0; } if (sx < 0) { w += sx; dx -= sx; sx = 0; } if (sy < 0) { h += sy; dy -= sy; sy = 0; } const int minX = jmin (dx, sx); const int minY = jmin (dy, sy); w = jmin (w, getWidth() - jmax (sx, dx)); h = jmin (h, getHeight() - jmax (sy, dy)); if (w > 0 && h > 0) { const int maxX = jmax (dx, sx) + w; const int maxY = jmax (dy, sy) + h; const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, BitmapData::readWrite); uint8* dst = destData.getPixelPointer (dx - minX, dy - minY); const uint8* src = destData.getPixelPointer (sx - minX, sy - minY); const size_t lineSize = (size_t) (destData.pixelStride * w); if (dy > sy) { while (--h >= 0) { const int offset = h * destData.lineStride; memmove (dst + offset, src + offset, lineSize); } } else if (dst != src) { while (--h >= 0) { memmove (dst, src, lineSize); dst += destData.lineStride; src += destData.lineStride; } } } }
ASFUNCTIONBODY(BitmapData,draw) { BitmapData* th = obj->as<BitmapData>(); if(th->disposed) throw Class<ArgumentError>::getInstanceS("Disposed BitmapData"); _NR<ASObject> drawable; _NR<Matrix> matrix; _NR<ColorTransform> ctransform; _NR<ASString> blendMode; _NR<Rectangle> clipRect; bool smoothing; ARG_UNPACK (drawable) (matrix, NullRef) (ctransform, NullRef) (blendMode, NullRef) (clipRect, NullRef) (smoothing, false); if(!drawable->getClass() || !drawable->getClass()->isSubClass(InterfaceClass<IBitmapDrawable>::getClass()) ) throw Class<TypeError>::getInstanceS("Error #1034: Wrong type"); if(!ctransform.isNull() || !blendMode.isNull() || !clipRect.isNull() || smoothing) LOG(LOG_NOT_IMPLEMENTED,"BitmapData.draw does not support many parameters"); if(drawable->is<BitmapData>()) { BitmapData* data=drawable->as<BitmapData>(); //Compute the initial matrix, if any MATRIX initialMatrix; if(!matrix.isNull()) initialMatrix=matrix->getMATRIX(); CairoRenderContext ctxt(th->getData(), th->width, th->height); //Blit the data while transforming it ctxt.transformedBlit(initialMatrix, data->getData(), data->getWidth(), data->getHeight(), CairoRenderContext::FILTER_NONE); } else if(drawable->is<DisplayObject>()) { //Create an InvalidateQueue to store all the hierarchy of objects that must be drawn SoftwareInvalidateQueue queue; DisplayObject* d=drawable->as<DisplayObject>(); d->requestInvalidation(&queue); CairoRenderContext ctxt(th->getData(), th->width, th->height); //Compute the initial matrix, if any MATRIX initialMatrix; if(!matrix.isNull()) initialMatrix=matrix->getMATRIX(); for(auto it=queue.queue.begin();it!=queue.queue.end();it++) { DisplayObject* target=(*it).getPtr(); //Get the drawable from each of the added objects IDrawable* drawable=target->invalidate(d, initialMatrix); if(drawable==NULL) continue; //Compute the matrix for this object uint8_t* buf=drawable->getPixelBuffer(); //Construct a CachedSurface using the data CachedSurface& surface=ctxt.allocateCustomSurface(target,buf); surface.tex.width=drawable->getWidth(); surface.tex.height=drawable->getHeight(); surface.xOffset=drawable->getXOffset(); surface.yOffset=drawable->getYOffset(); delete drawable; } d->Render(ctxt, false); } else LOG(LOG_NOT_IMPLEMENTED,"BitmapData.draw does not support " << drawable->toDebugString()); th->notifyUsers(); return NULL; }